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

#include <dlfcn.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/signalfd.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <signal.h>
#include <unistd.h>
#include <sys/prctl.h>
#include <sched.h>
#include "appspawn.h"
#include "appspawn_hook.h"
#include "appspawn_modulemgr.h"
#include "appspawn_manager.h"
#include "appspawn_msg.h"
#include "appspawn_server.h"
#include "appspawn_trace.h"
#include "appspawn_utils.h"
#include "init_socket.h"
#include "init_utils.h"
#include "parameter.h"
#include "appspawn_adapter.h"
#include "securec.h"
#include "cJSON.h"
#ifdef APPSPAWN_HISYSEVENT
#include "hisysevent_adapter.h"
#endif
#define PARAM_BUFFER_SIZE 10
#define PATH_SIZE 256
#define FD_PATH_SIZE 128
#define UNLOCK_MOUNT_TIMEOUT_MS 30000  // 30s timeout for unlock mount

#define PREFORK_PROCESS "apppool"
#define APPSPAWN_MSG_USER_CHECK_COUNT 4
#define USER_ID_MIN_VALUE 100
#define USER_ID_MAX_VALUE 10736
#define LOCK_STATUS_PARAM_SIZE 64
#ifndef PIDFD_NONBLOCK
#define PIDFD_NONBLOCK O_NONBLOCK
#endif

static void WaitChildTimeout(const TimerHandle taskHandle, void *context);
static void ProcessChildResponse(const WatcherHandle taskHandle, int fd, uint32_t *events, const void *context);
static void WaitChildDied(pid_t pid, int status);
static void OnReceiveRequest(const TaskHandle taskHandle, const uint8_t *buffer, uint32_t buffLen);
static void ProcessRecvMsg(AppSpawnConnection *connection, AppSpawnMsgNode *message);
static void UnlockChildTimeout(const TimerHandle taskHandle, void *context);
static void ProcessUnlockChildResponse(const WatcherHandle taskHandle, int fd,
    uint32_t *events, const void *context);

// Forward declarations for unlock mount functions
APPSPAWN_STATIC bool HandleUnlockEvent(AppSpawnContent *content, int uid, AppSpawnMsgNode *message,
    AppSpawnConnection *connection);
APPSPAWN_STATIC int SendUnlockMsgToPrefork(AppSpawnContent *content, int uid);
APPSPAWN_STATIC int DoUnlockMountSerial(AppSpawnContent *content, int uid);
APPSPAWN_STATIC int ProcessUnlockMessage(int uid);
APPSPAWN_STATIC int ForkAndDoUnlockMount(AppSpawnContent *content, int uid, AppSpawningCtx *property);

// FD_CLOEXEC
static inline void SetFdCtrl(int fd, int opt)
{
    int option = fcntl(fd, F_GETFD);
    APPSPAWN_CHECK(option >= 0, return, "SetFdCtrl fcntl failed %{public}d, %{public}d", option, errno);
    int ret = fcntl(fd, F_SETFD, (unsigned int)option | (unsigned int)opt);
    if (ret < 0) {
        APPSPAWN_LOGI("Set fd %{public}d option %{public}d %{public}d result: %{public}d", fd, option, opt, errno);
    }
}

static void AppQueueDestroyProc(const AppSpawnMgr *mgr, AppSpawnedProcess *appInfo, void *data)
{
    pid_t pid = appInfo->pid;
    APPSPAWN_LOGI("kill %{public}s pid: %{public}d", appInfo->name, appInfo->pid);
    // notify child proess died,clean sandbox info
    ProcessMgrHookExecute(STAGE_SERVER_APP_DIED, GetAppSpawnContent(), appInfo);
    ProcessMgrHookExecute(STAGE_SERVER_APP_CLEANUP, GetAppSpawnContent(), appInfo);
    OH_ListRemove(&appInfo->node);
    OH_ListInit(&appInfo->node);
    free(appInfo);
    if (pid > 0 && kill(pid, SIGKILL) != 0) {
        APPSPAWN_LOGE("unable to kill process, pid: %{public}d errno: %{public}d", pid, errno);
    }
}

APPSPAWN_STATIC void StopAppSpawn(void)
{
    AppSpawnContent *content = GetAppSpawnContent();
    if (content != NULL && content->reservedPid > 0) {
        // Snapshot reservedPid before clearing, needed for CleanupSpawningFdsByPid.
        // Avoid accessing content->reservedPid after kill since the signal handler
        // (HandleDiedPid) may zero it concurrently.
        pid_t reservedPid = content->reservedPid;
        int ret = kill(reservedPid, SIGKILL);
        APPSPAWN_CHECK_ONLY_LOG(ret == 0, "kill reserved pid %{public}d failed %{public}d %{public}d",
            reservedPid, ret, errno);
        content->reservedPid = 0;
        // Cleanup spawning fds (prefork pipe + parent-child pipe) for the killed child.
        // This frees both TYPE_CHILD_PARENT and TYPE_PARENT_CHILD nodes from the queue.
        CleanupSpawningFdsByPid((AppSpawnMgr *)content, reservedPid);
    }
    TraversalSpawnedProcess(AppQueueDestroyProc, NULL);
    APPSPAWN_LOGI("StopAppSpawn ");
#ifdef APPSPAWN_HISYSEVENT
    AppSpawnHiSysEventWrite();
#endif
    LE_StopLoop(LE_GetDefaultLoop());
}

static inline void DumpStatus(const char *appName, pid_t pid, int status, int *signal)
{
    if (WIFSIGNALED(status)) {
        *signal = WTERMSIG(status);
        APPSPAWN_LOGW("%{public}s with pid %{public}d exit with signal:%{public}d", appName, pid, *signal);
    }
    if (WIFEXITED(status)) {
        *signal = WEXITSTATUS(status);
        APPSPAWN_LOGW("%{public}s with pid %{public}d exit with code:%{public}d", appName, pid, *signal);
    }
}

APPSPAWN_STATIC void WriteSignalInfoToFd(AppSpawnedProcess *appInfo, AppSpawnContent *content, int signal)
{
    APPSPAWN_CHECK(content->signalFd > 0, return, "Invalid signal fd[%{public}d]", content->signalFd);
    APPSPAWN_CHECK(appInfo->pid > 0, return, "Invalid pid[%{public}d]", appInfo->pid);
    APPSPAWN_CHECK(appInfo->uid > 0, return, "Invalid uid[%{public}d]", appInfo->uid);
    APPSPAWN_CHECK(appInfo->name != NULL, return, "Invalid name");

    cJSON *root = cJSON_CreateObject();
    if (root == NULL) {
        APPSPAWN_LOGE("signal json write create root object unsuccess");
        return;
    }
    cJSON_AddNumberToObject(root, "pid", appInfo->pid);
    cJSON_AddNumberToObject(root, "uid", appInfo->uid);
    cJSON_AddNumberToObject(root, "signal", signal);
    cJSON_AddStringToObject(root, "bundleName", appInfo->name);
    char *jsonString = cJSON_Print(root);
    cJSON_Delete(root);
    APPSPAWN_CHECK(jsonString != NULL, return, "jsonString is NULL");
    int ret = write(content->signalFd, jsonString, strlen(jsonString) + 1);
    if (ret < 0) {
        free(jsonString);
        APPSPAWN_LOGE("Spawn Listen failed to write signal info to fd errno %{public}d", errno);
        return;
    }
    APPSPAWN_LOGV("Spawn Listen write signal info %{public}s to fd %{public}d success", jsonString, content->signalFd);
    free(jsonString);
}

APPSPAWN_STATIC void HandleDiedPid(pid_t pid, uid_t uid, int status)
{
    AppSpawnContent *content = GetAppSpawnContent();
    APPSPAWN_CHECK(content != NULL, return, "Invalid content");

    if (pid == content->reservedPid) {
        APPSPAWN_LOGW("HandleDiedPid with reservedPid %{public}d", pid);
        // Prefork child died unexpectedly (crash, signal, etc.).
        // Cleanup spawning fds so the parent doesn't hold stale pipe fds.
        CleanupSpawningFdsByPid((AppSpawnMgr *)content, pid);
        content->reservedPid = 0;
    }
    int signal = 0;
    AppSpawnedProcess *appInfo = GetSpawnedProcess(pid);
    if (appInfo == NULL) { // If an exception occurs during app spawning, kill pid, return failed
        WaitChildDied(pid, status);
        DumpStatus("unknown", pid, status, &signal);
        return;
    }

    appInfo->exitStatus = status;
    APPSPAWN_CHECK_ONLY_LOG(appInfo->uid == uid, "Invalid uid %{public}u %{public}u", appInfo->uid, uid);
    DumpStatus(appInfo->name, pid, status, &signal);
    WriteSignalInfoToFd(appInfo, content, signal);
    ProcessMgrHookExecute(STAGE_SERVER_APP_DIED, GetAppSpawnContent(), appInfo);
    ProcessMgrHookExecute(STAGE_SERVER_APP_CLEANUP, GetAppSpawnContent(), appInfo);

    // delete appinfo form appQueue, and move appinfo to diedQueue for nwebspawn
    TerminateSpawnedProcess(appInfo);
}

APPSPAWN_STATIC void ProcessSignal(const struct signalfd_siginfo *siginfo)
{
    APPSPAWN_DUMPI("ProcessSignal signum %{public}d %{public}d", siginfo->ssi_signo, siginfo->ssi_pid);
    switch (siginfo->ssi_signo) {
        case SIGCHLD: { // delete pid from app map
            pid_t pid;
            int status;
            while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
                APPSPAWN_CHECK(WIFSIGNALED(status) || WIFEXITED(status), return,
                    "ProcessSignal with wrong status:%{public}d", status);
                HandleDiedPid(pid, siginfo->ssi_uid, status);
            }
#if (defined(CJAPP_SPAWN) || defined(NATIVE_SPAWN))
            if (OH_ListGetCnt(&GetAppSpawnMgr()->appQueue) == 0 &&
                OH_ListGetCnt(&GetAppSpawnMgr()->appSpawnQueue) == 0) {
                LE_StopLoop(LE_GetDefaultLoop());
            }
#endif
            break;
        }
        case SIGTERM: { // appswapn killed, use kill without parameter
            StopAppSpawn();
            break;
        }
        default:
            APPSPAWN_LOGI("SigHandler, unsupported signal %{public}d.", siginfo->ssi_signo);
            break;
    }
}

static void AppSpawningCtxOnClose(const AppSpawnMgr *mgr, AppSpawningCtx *ctx, void *data)
{
    if (ctx == NULL || ctx->message == NULL || ctx->message->connection != data) {
        return;
    }
    APPSPAWN_LOGI("Kill process, pid: %{public}d app: %{public}s", ctx->pid, GetProcessName(ctx));
    if (ctx->pid > 0 && kill(ctx->pid, SIGKILL) != 0) {
        APPSPAWN_LOGE("unable to kill process, pid: %{public}d errno: %{public}d", ctx->pid, errno);
    }
    AppSpawnHookExecute(STAGE_SERVER_SPAWN_ABORT, 0, GetAppSpawnContent(), &ctx->client);
    DeleteAppSpawningCtx(ctx);
}

static void OnClose(const TaskHandle taskHandle)
{
    int fd = LE_GetSocketFd(taskHandle);
    APPSPAWN_LOGV("OnClose socket fd %{public}d", fd);
    if (!IsSpawnServer(GetAppSpawnMgr())) {
        if (fd > 0) {
            close(fd);
            fd = -1;
        }
        return;
    }

    AppSpawnConnection *connection = (AppSpawnConnection *)LE_GetUserData(taskHandle);
    APPSPAWN_CHECK(connection != NULL, return, "Invalid connection");
    if (connection->receiverCtx.timer) {
        LE_StopTimer(LE_GetDefaultLoop(), connection->receiverCtx.timer);
        connection->receiverCtx.timer = NULL;
    }
    APPSPAWN_LOGI("OnClose connectionId: %{public}u socket %{public}d", connection->connectionId, fd);
    DeleteAppSpawnMsg(&connection->receiverCtx.incompleteMsg);
    connection->receiverCtx.incompleteMsg = NULL;
    // connect close, to close spawning app
    AppSpawningCtxTraversal(AppSpawningCtxOnClose, connection);
}

static void OnDisConnect(const TaskHandle taskHandle)
{
    AppSpawnConnection *connection = (AppSpawnConnection *)LE_GetUserData(taskHandle);
    APPSPAWN_CHECK(connection != NULL, return, "Invalid connection");
    APPSPAWN_LOGI("OnDisConnect connectionId: %{public}u socket %{public}d",
        connection->connectionId, LE_GetSocketFd(taskHandle));
    OnClose(taskHandle);
}

static void SendMessageComplete(const TaskHandle taskHandle, BufferHandle handle)
{
    AppSpawnConnection *connection = (AppSpawnConnection *)LE_GetUserData(taskHandle);
    APPSPAWN_CHECK(connection != NULL, return, "Invalid connection");
    uint32_t bufferSize = sizeof(AppSpawnResponseMsg);
    AppSpawnResponseMsg *msg = (AppSpawnResponseMsg *)LE_GetBufferInfo(handle, NULL, &bufferSize);
    if (msg == NULL) {
        return;
    }
    AppSpawnedProcess *appInfo = GetSpawnedProcess(msg->result.pid);
    if (appInfo == NULL) {
        return;
    }
    APPSPAWN_DUMPI("SendMessageComplete connectionId:%{public}u result:%{public}d app:%{public}s pid:%{public}d",
        connection->connectionId, LE_GetSendResult(handle), appInfo->name, msg->result.pid);
    if (LE_GetSendResult(handle) != 0 && msg->result.pid > 0) {
        kill(msg->result.pid, SIGKILL);
    }
}

static int SendResponse(const AppSpawnConnection *connection, const AppSpawnMsg *msg, int result, pid_t pid)
{
    APPSPAWN_LOGV("SendResponse connectionId: %{public}u result: 0x%{public}x pid: %{public}d",
        connection->connectionId, result, pid);
    uint32_t bufferSize = sizeof(AppSpawnResponseMsg);
    BufferHandle handle = LE_CreateBuffer(LE_GetDefaultLoop(), bufferSize);
    AppSpawnResponseMsg *buffer = (AppSpawnResponseMsg *)LE_GetBufferInfo(handle, NULL, &bufferSize);
    APPSPAWN_CHECK(buffer != NULL, return APPSPAWN_ERROR_UTILS_MEM_FAIL, "buffer is null");
    int ret = memcpy_s(buffer, bufferSize, msg, sizeof(AppSpawnMsg));
    APPSPAWN_CHECK(ret == 0, LE_FreeBuffer(LE_GetDefaultLoop(), NULL, handle);
        return -1, "Failed to memcpy_s bufferSize");
    buffer->result.result = result;
    buffer->result.pid = pid;
    buffer->result.checkPointId = 0;
    return LE_Send(LE_GetDefaultLoop(), connection->stream, handle, bufferSize);
}

/**
 * @brief 发送扩展响应(包含checkpoint信息)
 *
 * @param connection 连接对象
 * @param msg 消息头
 * @param result 结果码
 * @param pid 进程PID
 * @param checkPointId checkpoint ID
 * @return 成功返回0,失败返回错误码
 */
static int SendResponseEx(const AppSpawnConnection *connection, const AppSpawnMsg *msg,
                          int result, pid_t pid, uint64_t checkPointId)
{
    APPSPAWN_LOGI("SendResponseEx connectionId: %{public}u result: 0x%{public}x pid: %{public}d "
                  "checkPointId: %{public}" PRId64"", connection->connectionId, result, pid, checkPointId);
    uint32_t bufferSize = sizeof(AppSpawnResponseMsg);
    BufferHandle handle = LE_CreateBuffer(LE_GetDefaultLoop(), bufferSize);
    AppSpawnResponseMsg *buffer = (AppSpawnResponseMsg *)LE_GetBufferInfo(handle, NULL, &bufferSize);
    APPSPAWN_CHECK(buffer != NULL, return APPSPAWN_ERROR_UTILS_MEM_FAIL, "buffer is null");
    int ret = memcpy_s(buffer, bufferSize, msg, sizeof(AppSpawnMsg));
    APPSPAWN_CHECK(ret == 0, LE_FreeBuffer(LE_GetDefaultLoop(), NULL, handle);
        return -1, "Failed to memcpy_s bufferSize");
    buffer->result.result = result;
    buffer->result.pid = pid;
    buffer->result.checkPointId = checkPointId;
    return LE_Send(LE_GetDefaultLoop(), connection->stream, handle, bufferSize);
}

static void WaitMsgCompleteTimeOut(const TimerHandle taskHandle, void *context)
{
    AppSpawnConnection *connection = (AppSpawnConnection *)context;
    APPSPAWN_LOGE("Long time no msg complete so close connectionId: %{public}u", connection->connectionId);
    DeleteAppSpawnMsg(&connection->receiverCtx.incompleteMsg);
    connection->receiverCtx.incompleteMsg = NULL;
    LE_CloseStreamTask(LE_GetDefaultLoop(), connection->stream);
}

static inline int StartTimerForCheckMsg(AppSpawnConnection *connection)
{
    if (connection->receiverCtx.timer != NULL) {
        return 0;
    }
    int ret = LE_CreateTimer(LE_GetDefaultLoop(), &connection->receiverCtx.timer, WaitMsgCompleteTimeOut, connection);
    if (ret == 0) {
        ret = LE_StartTimer(LE_GetDefaultLoop(), connection->receiverCtx.timer, MAX_WAIT_MSG_COMPLETE, 1);
    }
    return ret;
}

static int HandleRecvMessage(const TaskHandle taskHandle, uint8_t * buffer, int bufferSize, int flags)
{
    int socketFd = LE_GetSocketFd(taskHandle);
    struct iovec iov = {
        .iov_base = buffer,
        .iov_len = bufferSize,
    };
    char ctrlBuffer[CMSG_SPACE(APP_MAX_FD_COUNT * sizeof(int))];
    struct msghdr msg = {
        .msg_iov = &iov,
        .msg_iovlen = 1,
        .msg_control = ctrlBuffer,
        .msg_controllen = sizeof(ctrlBuffer),
    };

    AppSpawnConnection *connection = (AppSpawnConnection *) LE_GetUserData(taskHandle);
    APPSPAWN_CHECK(connection != NULL, return -1, "Invalid connection");
    errno = 0;
    int recvLen = recvmsg(socketFd, &msg, flags);
    APPSPAWN_CHECK_ONLY_LOG(errno == 0, "recvmsg with errno %{public}d", errno);
    struct cmsghdr *cmsg = NULL;
    for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
        if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
            int fdCount = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
            int *fd = (int *) CMSG_DATA(cmsg);
            APPSPAWN_CHECK(fdCount <= APP_MAX_FD_COUNT, return -1,
                "failed to recv fd %{public}d %{public}d", connection->receiverCtx.fdCount, fdCount);
            int ret = memcpy_s(connection->receiverCtx.fds,
                fdCount * sizeof(int), fd, fdCount * sizeof(int));
            APPSPAWN_CHECK(ret == 0, return -1, "memcpy_s fd ret %{public}d", ret);
            connection->receiverCtx.fdCount = fdCount;
        }
    }

    return recvLen;
}

APPSPAWN_STATIC bool OnConnectionUserCheck(uid_t uid)
{
    const uid_t uids[APPSPAWN_MSG_USER_CHECK_COUNT] = {
        0, // root 0
        3350, // app_fwk_update 3350
        5523, // foundation 5523
        1090, //storage_manager 1090
    };

    for (int i = 0; i < APPSPAWN_MSG_USER_CHECK_COUNT; i++) {
        if (uid == uids[i]) {
            return true;
        }
    }

    // shell 2000
    if (uid == 2000 && IsDeveloperModeOpen()) {
        return true;
    }

    return false;
}

static int OnConnection(const LoopHandle loopHandle, const TaskHandle server)
{
    APPSPAWN_CHECK(server != NULL && loopHandle != NULL, return -1, "Error server");
    static uint32_t connectionId = 0;
    TaskHandle stream;
    LE_StreamInfo info = {};
    info.baseInfo.flags = TASK_STREAM | TASK_PIPE | TASK_CONNECT;
    info.baseInfo.close = OnClose;
    info.baseInfo.userDataSize = sizeof(AppSpawnConnection);
    info.disConnectComplete = OnDisConnect;
    info.sendMessageComplete = SendMessageComplete;
    info.recvMessage = OnReceiveRequest;
    info.handleRecvMsg = HandleRecvMessage;
    LE_STATUS ret = LE_AcceptStreamClient(loopHandle, server, &stream, &info);
    APPSPAWN_CHECK(ret == 0, return -1, "Failed to alloc stream");

    AppSpawnConnection *connection = (AppSpawnConnection *)LE_GetUserData(stream);
    APPSPAWN_CHECK(connection != NULL, return -1, "Failed to alloc stream");
    struct ucred cred = {-1, -1, -1};
    socklen_t credSize = sizeof(struct ucred);
    if ((getsockopt(LE_GetSocketFd(stream), SOL_SOCKET, SO_PEERCRED, &cred, &credSize) < 0) ||
        !OnConnectionUserCheck(cred.uid)) {
        APPSPAWN_LOGE("Invalid uid %{public}d from client", cred.uid);
        LE_CloseStreamTask(LE_GetDefaultLoop(), stream);
        return -1;
    }
    SetFdCtrl(LE_GetSocketFd(stream), FD_CLOEXEC);

    connection->connectionId = ++connectionId;
    connection->stream = stream;
    connection->receiverCtx.fdCount = 0;
    connection->receiverCtx.incompleteMsg = NULL;
    connection->receiverCtx.timer = NULL;
    connection->receiverCtx.msgRecvLen = 0;
    connection->receiverCtx.nextMsgId = 1;
    APPSPAWN_LOGI("OnConnection connectionId: %{public}u fd %{public}d ",
        connection->connectionId, LE_GetSocketFd(stream));
    return 0;
}

static void OnListenFdClose(const TaskHandle taskHandle)
{
    /**
     * The socket fd of the parent process appspawn is executed in the AppSpawnDestroyContent function,
     * and executing it in the OnListen function will result in a badfd
     */
    if (IsSpawnServer(GetAppSpawnMgr())) {
        return;
    }
    // close socket listen fd in child process
    int fd = LE_GetSocketFd(taskHandle);
    APPSPAWN_LOGV("OnListenFdClose listen fd: %{public}d", fd);
    if (fd > 0) {
        close(fd);
        fd = -1;
    }
}

APPSPAWN_STATIC bool MsgDevicedebugCheck(TaskHandle stream, AppSpawnMsgNode *message)
{
    struct ucred cred = {0, 0, 0};
    socklen_t credSize = sizeof(cred);
    if (getsockopt(LE_GetSocketFd(stream), SOL_SOCKET, SO_PEERCRED, &cred, &credSize) < 0) {
        return false;
    }

    if (cred.uid != DecodeUid("shell")) {
        return true;
    }

    AppSpawnMsg *msg = &message->msgHeader;
    APPSPAWN_CHECK(msg->msgType == MSG_DEVICE_DEBUG, return false,
        "appspawn devicedebug msg type is not devicedebug [%{public}d]", msg->msgType);

    return true;
}

static void OnReceiveRequest(const TaskHandle taskHandle, const uint8_t *buffer, uint32_t buffLen)
{
    AppSpawnConnection *connection = (AppSpawnConnection *)LE_GetUserData(taskHandle);
    APPSPAWN_CHECK(connection != NULL, LE_CloseTask(LE_GetDefaultLoop(), taskHandle);
        return, "Failed to get client form socket");
    APPSPAWN_CHECK(buffLen < MAX_MSG_TOTAL_LENGTH, LE_CloseTask(LE_GetDefaultLoop(), taskHandle);
        return, "Message too long %{public}u", buffLen);

    uint32_t reminder = 0;
    uint32_t currLen = 0;
    AppSpawnMsgNode *message = connection->receiverCtx.incompleteMsg; // incomplete msg
    connection->receiverCtx.incompleteMsg = NULL;
    int ret = 0;
    do {
        APPSPAWN_DUMPI("connectionId:%{public}u buffLen:%{public}d", connection->connectionId, buffLen - currLen);

        ret = GetAppSpawnMsgFromBuffer(buffer + currLen, buffLen - currLen,
            &message, &connection->receiverCtx.msgRecvLen, &reminder);
        APPSPAWN_CHECK_ONLY_EXPER(ret == 0, break);

        if (connection->receiverCtx.msgRecvLen != message->msgHeader.msgLen) {  // recv complete msg
            connection->receiverCtx.incompleteMsg = message;
            message = NULL;
            break;
        }
        connection->receiverCtx.msgRecvLen = 0;
        if (connection->receiverCtx.timer) {
            LE_StopTimer(LE_GetDefaultLoop(), connection->receiverCtx.timer);
            connection->receiverCtx.timer = NULL;
        }

        APPSPAWN_CHECK_ONLY_EXPER(MsgDevicedebugCheck(connection->stream, message),
            LE_CloseTask(LE_GetDefaultLoop(), taskHandle); return);

        // decode msg
        ret = DecodeAppSpawnMsg(message);
        APPSPAWN_CHECK_ONLY_EXPER(ret == 0, break);
        StartAppspawnTrace("ProcessRecvMsg");
        (void)ProcessRecvMsg(connection, message);
        FinishAppspawnTrace();
        message = NULL;
        currLen = buffLen - reminder;
    } while (reminder > 0);

    if (message) {
        DeleteAppSpawnMsg(&message);
    }
    if (ret != 0) {
        LE_CloseTask(LE_GetDefaultLoop(), taskHandle);
        return;
    }
    if (connection->receiverCtx.incompleteMsg != NULL) { // Start the detection timer
        ret = StartTimerForCheckMsg(connection);
        APPSPAWN_CHECK(ret == 0, LE_CloseStreamTask(LE_GetDefaultLoop(), taskHandle);
            return, "Failed to create time for connection");
    }
}

APPSPAWN_STATIC char *GetSpawnNameByRunMode(RunMode mode)
{
    if (mode == MODE_FOR_APP_SPAWN || mode == MODE_FOR_APP_COLD_RUN) {
        return APPSPAWN_SERVER_NAME;
    } else if (mode == MODE_FOR_NWEB_SPAWN || mode == MODE_FOR_NWEB_COLD_RUN) {
        return NWEBSPAWN_SERVER_NAME;
    } else if (mode == MODE_FOR_HYBRID_SPAWN || mode == MODE_FOR_HYBRID_COLD_RUN) {
        return HYBRIDSPAWN_SERVER_NAME;
    } else if (mode == MODE_FOR_NATIVE_SPAWN || mode == MODE_FOR_NATIVE_COLD_RUN) {
        return NATIVESPAWN_SERVER_NAME;
    } else if (mode == MODE_FOR_CJAPP_SPAWN || mode == MODE_FOR_CJAPP_COLD_RUN) {
        return CJAPPSPAWN_SERVER_NAME;
    }
    return "";
}

APPSPAWN_STATIC char *GetMapMem(uint32_t clientId, const char *processName,
    uint32_t size, bool readOnly, RunMode runMode)
{
    char path[PATH_MAX] = {};
    int len = sprintf_s(path, sizeof(path), APPSPAWN_MSG_DIR "%s/%s_%u",
        GetSpawnNameByRunMode(runMode), processName, clientId);
    APPSPAWN_CHECK(len > 0, return NULL, "Failed to format path %{public}s", processName);
    APPSPAWN_LOGV("GetMapMem for child %{public}s memSize %{public}u", path, size);
    int prot = PROT_READ;
    int mode = O_RDONLY;
    if (!readOnly) {
        mode = O_CREAT | O_RDWR | O_TRUNC;
        prot = PROT_READ | PROT_WRITE;
    }
    int fd = open(path, mode, S_IRWXU);
    APPSPAWN_CHECK(fd >= 0, return NULL, "Failed to open errno %{public}d path %{public}s", errno, path);
    if (!readOnly) {
        APPSPAWN_CHECK(fallocate(fd, 0, 0, size) == 0, close(fd);
            return NULL, "failed fallocate %{public}s %{public}d", processName, errno);
    }
    void *areaAddr = (void *)mmap(NULL, size, prot, MAP_SHARED, fd, 0);
    close(fd);
    APPSPAWN_CHECK(areaAddr != MAP_FAILED && areaAddr != NULL,
        return NULL, "Failed to map memory error %{public}d fileName %{public}s ", errno, path);
    return (char *)areaAddr;
}

APPSPAWN_STATIC int WriteMsgToChild(AppSpawningCtx *property, RunMode mode)
{
    APPSPAWN_CHECK(property != NULL && property->message != NULL, return APPSPAWN_MSG_INVALID,
        "Failed to WriteMsgToChild property invalid");
    const uint32_t memSize = (property->message->msgHeader.msgLen / 4096 + 1) * 4096; // 4096 4K
    char *buffer = GetMapMem(property->client.id, GetProcessName(property), memSize, false, mode);
    APPSPAWN_CHECK(buffer != NULL, return APPSPAWN_SYSTEM_ERROR,
        "Failed to map memory error %{public}d fileName %{public}s ", errno, GetProcessName(property));
    // copy msg header
    int ret = memcpy_s(buffer, memSize, &property->message->msgHeader, sizeof(AppSpawnMsg));
    if (ret == 0) {
        ret = memcpy_s((char *)buffer + sizeof(AppSpawnMsg), memSize - sizeof(AppSpawnMsg),
            property->message->buffer, property->message->msgHeader.msgLen - sizeof(AppSpawnMsg));
    }
    if (ret != 0) {
        APPSPAWN_LOGE("Failed to copy msg fileName %{public}s ", GetProcessName(property));
        munmap((char *)buffer, memSize);
        return APPSPAWN_SYSTEM_ERROR;
    }
    property->forkCtx.msgSize = memSize;
    property->forkCtx.childMsg = buffer;
    APPSPAWN_LOGV("Write msg to child: %{public}u success", property->client.id);
    return 0;
}

/**
 * @brief Initialize fork context by creating a non-blocking pipe.
 *
 * Creates a pipe for fork result notification (child writes, parent reads).
 * The read end is set to O_NONBLOCK so the parent can poll without blocking.
 *
 * @param property  Spawning context to initialize
 * @return 0 on success, errno on pipe creation failure
 */
static int InitForkContext(AppSpawningCtx *property)
{
    if (pipe(property->forkCtx.fd) == -1) {
        APPSPAWN_LOGE("create pipe fail, errno: %{public}d", errno);
        return errno;
    }
    // Set read end to non-blocking: use F_GETFL/F_SETFL for file status flags (O_NONBLOCK),
    // not F_GETFD/F_SETFD which operate on fd flags (only FD_CLOEXEC).
    int flags = fcntl(property->forkCtx.fd[0], F_GETFL);
    if (flags >= 0) {
        (void)fcntl(property->forkCtx.fd[0], F_SETFL, (unsigned int)flags | O_NONBLOCK);
    }
    return 0;
}

static void ClosePidfdWatcher(const TaskHandle taskHandle)
{
    int fd = LE_GetSocketFd(taskHandle);
    if (fd >= 0) {
        close(fd);
    }
    void *p = LE_GetUserData(taskHandle);
    if (p != NULL) {
        free(*(void **)p);
    }
}

static void ProcessChildProcessFd(const WatcherHandle taskHandle, int fd, uint32_t *events, const void *context)
{
    APPSPAWN_CHECK_ONLY_EXPER(context != NULL, return);
    pid_t pid = *(pid_t *)context;
    APPSPAWN_DUMPI("Clear process group with pid:%{public}d,pidFd:%{public}d", pid, fd);
    AppSpawnedProcess *appInfo = GetSpawnedProcess(pid);
    if (appInfo == NULL) {
        APPSPAWN_LOGW("Cannot get app info by bundle name: %{public}d", pid);
        return;
    }
    ProcessMgrHookExecute(STAGE_SERVER_APP_DIED, GetAppSpawnContent(), appInfo);
    LE_CloseTask(LE_GetDefaultLoop(), taskHandle);
}

static int OpenPidFd(pid_t pid, unsigned int flags)
{
    return syscall(SYS_pidfd_open, pid, flags);
}

static void WatchChildProcessFd(AppSpawningCtx *property)
{
    if (property->pid <= 0) {
        APPSPAWN_LOGW("Invalid child process pid, skip watch");
        return;
    }
    if (IsNWebSpawnMode((AppSpawnMgr *)GetAppSpawnContent())) {
        APPSPAWN_LOGV("Nwebspawn don't need add pidfd");
        return;
    }
    AppSpawnedProcess *appInfo = GetSpawnedProcess(property->pid);
    if (appInfo == NULL) {
        APPSPAWN_LOGW("Cannot get app info of pid %{public}d", property->pid);
        return;
    }
    int fd = OpenPidFd(property->pid, PIDFD_NONBLOCK); // PIDFD_NONBLOCK  since Linux kernel 5.10
    if (fd < 0) {
        APPSPAWN_LOGW("Failed to open pid fd for app: %{public}s, err = %{public}d",
            GetBundleName(property), errno);
        return;
    }
    APPSPAWN_DUMPI("watch app process pid:%{public}d,pidFd:%{public}d", property->pid, fd);
    LE_WatchInfo watchInfo = {};
    watchInfo.fd = fd;
    watchInfo.flags = WATCHER_ONCE;
    watchInfo.events = EVENT_READ;
    watchInfo.close = ClosePidfdWatcher;
    watchInfo.processEvent = ProcessChildProcessFd;

    pid_t *appPid = (pid_t *)malloc(sizeof(pid_t));
    APPSPAWN_CHECK_ONLY_EXPER(appPid != NULL, return);
    *appPid = property->pid;
    LE_STATUS status = LE_StartWatcher(LE_GetDefaultLoop(), &property->forkCtx.pidFdWatcherHandle, &watchInfo, appPid);
    if (status != LE_SUCCESS) {
#ifndef APPSPAWN_TEST
        close(fd);
        free(appPid);
        appPid = NULL;
#endif
        APPSPAWN_LOGW("Failed to watch child pid fd, pid is %{public}d", property->pid);
    }
}

static int IsChildColdRun(AppSpawningCtx *property)
{
    return CheckAppMsgFlagsSet(property, APP_FLAGS_UBSAN_ENABLED) ||
        CheckAppMsgFlagsSet(property, APP_FLAGS_ASANENABLED) ||
        CheckAppMsgFlagsSet(property, APP_FLAGS_TSAN_ENABLED) ||
        CheckAppMsgFlagsSet(property, APP_FLAGS_HWASAN_ENABLED) ||
        (property->client.flags & APP_COLD_START);
}

static int AddChildWatcher(AppSpawningCtx *property)
{
    uint32_t defTimeout = IsChildColdRun(property) ? COLD_CHILD_RESPONSE_TIMEOUT : WAIT_CHILD_RESPONSE_TIMEOUT;
    uint32_t timeout = GetSpawnTimeout(defTimeout, IsChildColdRun(property));

    LE_WatchInfo watchInfo = {};
    watchInfo.fd = property->forkCtx.fd[0];
    watchInfo.flags = WATCHER_ONCE;
    watchInfo.events = EVENT_READ;
    watchInfo.processEvent = ProcessChildResponse;
    LE_STATUS status = LE_StartWatcher(LE_GetDefaultLoop(), &property->forkCtx.watcherHandle, &watchInfo, property);
    APPSPAWN_LOGV("AddChildWatcher with timeout %{public}u fd %{public}d", timeout, watchInfo.fd);
    APPSPAWN_CHECK(status == LE_SUCCESS,
        return APPSPAWN_SYSTEM_ERROR, "Failed to watch child %{public}d", property->pid);
    status = LE_CreateTimer(LE_GetDefaultLoop(), &property->forkCtx.timer, WaitChildTimeout, property);
    if (status == LE_SUCCESS) {
        status = LE_StartTimer(LE_GetDefaultLoop(), property->forkCtx.timer, timeout * 1000, 0); // 1000 1s
    }
    if (status != LE_SUCCESS) {
        if (property->forkCtx.timer != NULL) {
            LE_StopTimer(LE_GetDefaultLoop(), property->forkCtx.timer);
        }
        property->forkCtx.timer = NULL;
        LE_RemoveWatcher(LE_GetDefaultLoop(), property->forkCtx.watcherHandle);
        property->forkCtx.watcherHandle = NULL;
        APPSPAWN_LOGE("Failed to watch child %{public}d", property->pid);
        return APPSPAWN_SYSTEM_ERROR;
    }
    return 0;
}

static bool IsSupportRunHnp()
{
    char buffer[PARAM_BUFFER_SIZE] = {0};
    int ret = GetParameter("const.startup.hnp.execute.enable", "false", buffer, PARAM_BUFFER_SIZE);
    if (ret <= 0) {
        APPSPAWN_LOGE("Get hnp execute enable param unsuccess! ret =%{public}d", ret);
        return false;
    }

    if (strcmp(buffer, "true") == 0) {
        return true;
    }

    return false;
}

APPSPAWN_STATIC void ClearMMAP(int clientId, uint32_t memSize)
{
    AppSpawnContent *content = GetAppSpawnContent();
    if (content != NULL && content->propertyBuffer != NULL) {
        int ret = munmap(content->propertyBuffer, memSize);
        APPSPAWN_CHECK_ONLY_LOG(ret == 0, "munmap failed %{public}d %{public}d", ret, errno);
        content->propertyBuffer = NULL;
    }

    char path[PATH_MAX] = {0};
    int ret = snprintf_s(path, sizeof(path), sizeof(path) - 1, APPSPAWN_MSG_DIR "appspawn/prefork_%d", clientId);
    APPSPAWN_CHECK_ONLY_LOG(ret > 0, "snprintf failed with %{public}d %{public}d", ret, errno);
    if (ret > 0 && access(path, F_OK) == 0) {
        ret = unlink(path);
        APPSPAWN_CHECK_ONLY_LOG(ret == 0, "prefork unlink result %{public}d %{public}d", ret, errno);
    }
}

APPSPAWN_STATIC void ClearPreforkInfo(AppSpawningCtx *property)
{
    APPSPAWN_CHECK(property != NULL, return, "invalid property");

    if (property->forkCtx.childMsg != NULL) {
        int ret = munmap(property->forkCtx.childMsg, property->forkCtx.msgSize);
        APPSPAWN_CHECK_ONLY_LOG(ret == 0, "munmap failed %{public}d %{public}d", ret, errno);
        property->forkCtx.childMsg = NULL;
    }

    char path[PATH_MAX] = {0};
    int ret = snprintf_s(path, sizeof(path), sizeof(path) - 1, APPSPAWN_MSG_DIR "appspawn/prefork_%u",
        property->client.id);
    APPSPAWN_CHECK_ONLY_LOG(ret > 0, "snprintf failed with %{public}d %{public}d", ret, errno);
    if (ret > 0 && access(path, F_OK) == 0) {
        ret = unlink(path);
        APPSPAWN_CHECK_ONLY_LOG(ret == 0, "prefork unlink result %{public}d %{public}d", ret, errno);
    }
}

APPSPAWN_STATIC int WritePreforkMsg(AppSpawningCtx *property, uint32_t memSize)
{
    AppSpawnContent *content = GetAppSpawnContent();
    if (content == NULL || content->propertyBuffer == NULL) {
        APPSPAWN_LOGE("buffer is null can not write propery");
        return  -1;
    }

    int ret = memcpy_s(content->propertyBuffer, memSize, &property->message->msgHeader, sizeof(AppSpawnMsg));
    if (ret != 0) {
        APPSPAWN_LOGE("memcpys_s msgHeader failed");
        ClearMMAP(property->client.id, memSize);
        return ret;
    }

    ret = memcpy_s((char *)content->propertyBuffer + sizeof(AppSpawnMsg), memSize - sizeof(AppSpawnMsg),
        property->message->buffer, property->message->msgHeader.msgLen - sizeof(AppSpawnMsg));
    if (ret != 0) {
        APPSPAWN_LOGE("memcpys_s AppSpawnMsg failed");
        ClearMMAP(property->client.id, memSize);
        return ret;
    }
    ret = munmap((char *)content->propertyBuffer, memSize);
    APPSPAWN_CHECK_ONLY_LOG(ret == 0, "munmap failed %{public}d,%{public}d", ret, errno);
    content->propertyBuffer = NULL;
    return ret;
}

APPSPAWN_STATIC int GetAppSpawnMsg(AppSpawningCtx *property, uint32_t memSize, RunMode mode)
{
    uint8_t *buffer = (uint8_t *)GetMapMem(property->client.id, "prefork", memSize, true, mode);
    APPSPAWN_CHECK(buffer != NULL, return -1, "prefork buffer is null can not write propery");

    uint32_t msgRecvLen = 0;
    uint32_t remainLen = 0;
    property->forkCtx.childMsg = (char *)buffer;
    property->forkCtx.msgSize = memSize;
    AppSpawnMsgNode *message = NULL;
    int ret = GetAppSpawnMsgFromBuffer(buffer, ((AppSpawnMsg *)buffer)->msgLen, &message, &msgRecvLen, &remainLen);
    // release map
    APPSPAWN_LOGV("prefork GetAppSpawnMsg ret:%{public}d", ret);
    if (ret == 0 && DecodeAppSpawnMsg(message) == 0 && CheckAppSpawnMsg(message) == 0) {
        property->message = message;
        message = NULL;
        return 0;
    }
    DeleteAppSpawnMsg(&message);
    return -1;
}

APPSPAWN_STATIC int SetPreforkProcessName(AppSpawnContent *content)
{
    int ret = prctl(PR_SET_NAME, PREFORK_PROCESS);
    if (ret == -1) {
        return errno;
    }

    ret = memset_s(content->longProcName,
        (size_t)content->longProcNameLen, 0, (size_t)content->longProcNameLen);
    if (ret != EOK) {
        return EINVAL;
    }

    ret = strncpy_s(content->longProcName, content->longProcNameLen,
        PREFORK_PROCESS, strlen(PREFORK_PROCESS));
    if (ret != EOK) {
        return EINVAL;
    }

    return 0;
}

APPSPAWN_STATIC void ClearPipeFd(int pipe[], int length)
{
    for (int i = 0; i < length; i++) {
        if (pipe[i] > 0) {
            close(pipe[i]);
            pipe[i] = -1;
        }
    }
}

/**
 * @brief Set unlock mount result to system parameter
 * @param uid User ID
 * @param result Mount result (0 = success, non-zero = failure)
 */
APPSPAWN_STATIC void SetUnlockMountResult(int uid, int result)
{
    char unlockMountParam[LOCK_STATUS_PARAM_SIZE] = {0};
    int ret = snprintf_s(unlockMountParam, sizeof(unlockMountParam), sizeof(unlockMountParam) - 1,
        "startup.appspawn.unlock_mount.%d", uid);
    APPSPAWN_ONLY_EXPER(ret <= 0,
#ifdef APPSPAWN_HISYSEVENT
        ReportKeyEvent("UNLOCK_MOUNT_PARAM_FAIL");
#endif
        APPSPAWN_LOGE("snprintf_s failed for uid=%{public}d, ret=%{public}d", uid, ret);
        return);
    
    int setRet = SetParameter(unlockMountParam, result == 0 ? "0" : "1");
    APPSPAWN_ONLY_EXPER(setRet != 0,
#ifdef APPSPAWN_HISYSEVENT
        ReportKeyEvent("UNLOCK_MOUNT_PARAM_FAIL");
#endif
        APPSPAWN_LOGE("SetParameter failed for uid=%{public}d, ret=%{public}d", uid, setRet));
}

// ===== Unlock mount child watcher helpers (using AppSpawningCtx) =====
// Pattern mirrors AddChildWatcher / ProcessChildResponse / WaitChildTimeout for MSG_APP_SPAWN.

// AddUnlockChildWatcher: registers watcher + timer on forkCtx.fd[0].
// On failure, only cleans up watcher/timer resources it created.
// Caller is responsible for killing child and closing fd.
static int AddUnlockChildWatcher(AppSpawningCtx *property, uint64_t timeoutMs)
{
    APPSPAWN_CHECK(property != NULL, return APPSPAWN_SYSTEM_ERROR, "Invalid property");
    LE_WatchInfo watchInfo = {};
    watchInfo.fd = property->forkCtx.fd[0];
    watchInfo.flags = WATCHER_ONCE;
    watchInfo.events = EVENT_READ;
    watchInfo.processEvent = ProcessUnlockChildResponse;
    APPSPAWN_LOGI("AddUnlockChildWatcher fd=%{public}d pid=%{public}d",
        watchInfo.fd, property->pid);
    LE_STATUS status = LE_StartWatcher(LE_GetDefaultLoop(), &property->forkCtx.watcherHandle, &watchInfo, property);
    APPSPAWN_CHECK(status == LE_SUCCESS,
        return APPSPAWN_SYSTEM_ERROR, "Failed to start unlock watcher fd=%{public}d status=%{public}d",
        watchInfo.fd, status);

    status = LE_CreateTimer(LE_GetDefaultLoop(), &property->forkCtx.timer, UnlockChildTimeout, property);
    APPSPAWN_ONLY_EXPER(status == LE_SUCCESS,
        status = LE_StartTimer(LE_GetDefaultLoop(), property->forkCtx.timer, timeoutMs, 0));
    APPSPAWN_ONLY_EXPER(status == LE_SUCCESS, return 0);

    APPSPAWN_ONLY_EXPER(property->forkCtx.timer != NULL,
        LE_StopTimer(LE_GetDefaultLoop(), property->forkCtx.timer));
    property->forkCtx.timer = NULL;
    LE_RemoveWatcher(LE_GetDefaultLoop(), property->forkCtx.watcherHandle);
    property->forkCtx.watcherHandle = NULL;
    APPSPAWN_LOGE("Failed to start unlock timer, status=%{public}d", status);
    return APPSPAWN_SYSTEM_ERROR;
}

/**
 * @brief Watcher callback for processing unlock mount result from child process
 * @param taskHandle Watcher handle (unused)
 * @param fd Pipe read end file descriptor
 * @param events Event flags (unused)
 * @param context AppSpawningCtx pointer
 *
 * Called when child process writes result to pipe. Reads result from pipe,
 * sends response to client, and cleans up resources.
 */
static void ProcessUnlockChildResponse(const WatcherHandle taskHandle, int fd,
    uint32_t *events, const void *context)
{
    AppSpawningCtx *property = (AppSpawningCtx *)context;
    APPSPAWN_CHECK_ONLY_EXPER(property != NULL, return);
    property->forkCtx.watcherHandle = NULL;
    LE_RemoveWatcher(LE_GetDefaultLoop(), (WatcherHandle)taskHandle);

    int result = APPSPAWN_SYSTEM_ERROR;
    ssize_t readSize = read(fd, &result, sizeof(result));
    APPSPAWN_ONLY_EXPER(readSize != sizeof(result),
        APPSPAWN_LOGE("ProcessUnlockChildResponse read failed, ret=%{public}zd errno=%{public}d", readSize, errno);
        result = APPSPAWN_SYSTEM_ERROR);
    APPSPAWN_LOGI("ProcessUnlockChildResponse pid=%{public}d result=%{public}d", property->pid, result);

    APPSPAWN_ONLY_EXPER(property->message != NULL && property->message->connection != NULL,
        SendResponse(property->message->connection, &property->message->msgHeader, result, 0));

    property->message = NULL;
    DeleteAppSpawningCtx(property);
}

static void UnlockChildTimeout(const TimerHandle taskHandle, void *context)
{
    AppSpawningCtx *property = (AppSpawningCtx *)context;
    APPSPAWN_CHECK_ONLY_EXPER(property != NULL, return);
    APPSPAWN_LOGE("UnlockChildTimeout childPid=%{public}d", property->pid);
    APPSPAWN_ONLY_EXPER(property->pid > 0, kill(property->pid, SIGKILL));
    APPSPAWN_ONLY_EXPER(property->message != NULL && property->message->connection != NULL,
        SendResponse(property->message->connection, &property->message->msgHeader, APPSPAWN_SPAWN_TIMEOUT, 0));

    property->message = NULL;
    DeleteAppSpawningCtx(property);
}

APPSPAWN_STATIC void HandlePreforkUnlockMsg(AppSpawnContent *content, const AppSpawnUnlockMsg *unlockMsg)
{
    APPSPAWN_LOGI("HandlePreforkUnlockMsg: uid=%{public}d pid=%{public}d",
        unlockMsg->uid, getpid());
    int result = ProcessUnlockMessage(unlockMsg->uid);

    // Write result via param and report KEY_EVENT
    SetUnlockMountResult(unlockMsg->uid, result);
#ifdef APPSPAWN_HISYSEVENT
    ReportKeyEvent(result == 0 ? UNLOCK_SUCCESS : "UNLOCK_MOUNT_FAIL");
#endif
    // Write result back to parent via childToParentFd
    AppSpawnMgr *mgr = (AppSpawnMgr *)content;
    AppSpawnFds *childToParentFd = FindSpawningFdsByPid(mgr, getpid(), TYPE_CHILD_PARENT);
    APPSPAWN_ONLY_EXPER(childToParentFd == NULL || childToParentFd->fds[1] < 0,
        APPSPAWN_LOGE("L1 prefork childToParentFd not found, uid=%{public}d result=%{public}d dropped",
            unlockMsg->uid, result);
#ifdef APPSPAWN_HISYSEVENT
        ReportKeyEvent("UNLOCK_MOUNT_L1_RESULT_DROPPED");
#endif
        ProcessExit(0));

    APPSPAWN_LOGI("L1 prefork write result: uid=%{public}d result=%{public}d fd=%{public}d",
        unlockMsg->uid, result, childToParentFd->fds[1]);
    ssize_t writeSize = write(childToParentFd->fds[1], &result, sizeof(result));
    APPSPAWN_ONLY_EXPER(writeSize != sizeof(result),
#ifdef APPSPAWN_HISYSEVENT
        ReportKeyEvent("UNLOCK_MOUNT_L1_WRITE_FAIL");
#endif
        APPSPAWN_LOGE("prefork write result failed %{public}zd %{public}d", writeSize, errno));
    ProcessExit(0);
}

/**
 * @brief Handle FORK message in prefork child process.
 *
 * Called by PreforkChildLoop when a MSG_APP_SPAWN pipe message is received.
 * This function runs in the prefork child process context:
 * 1. Validates msgLen from the parent
 * 2. Sets up client identity (id, flags)
 * 3. Retrieves childToParentFd from spawningFdsQueue (registered by ForkAndRegisterFds)
 * 4. Reads app spawn message from shared memory (mmap)
 * 5. Calls AppSpawnChild to complete the actual app spawning
 *
 * @note This function never returns normally - always calls ProcessExit().
 */
APPSPAWN_STATIC void HandlePreforkForkMsg(AppSpawnContent *content, AppSpawningCtx *property,
    const AppSpawnPreforkMsg *preforkMsg)
{
    APPSPAWN_CHECK(preforkMsg != NULL && content != NULL && property != NULL, ProcessExit(0),
        "preforkMsg is NULL");
    // Validate message length: must fit in shared memory and contain at least AppSpawnMsg header
    if (preforkMsg->msgLen > MAX_MSG_TOTAL_LENGTH || preforkMsg->msgLen < sizeof(AppSpawnMsg)) {
        APPSPAWN_LOGE("prefork process invalid msgLen %{public}u", preforkMsg->msgLen);
        ProcessExit(0);
    }

    // Restore client identity from the pipe message
    property->client.id = preforkMsg->id;
    property->client.flags = preforkMsg->flags;
    property->isPrefork = true;

    // Retrieve prefork pipe fds from spawningFdsQueue (registered after fork in ForkAndRegisterFds)
    AppSpawnMgr *mgr = (AppSpawnMgr *)content;
    AppSpawnFds *pfFds = FindSpawningFdsByPid(mgr, getpid(), TYPE_CHILD_PARENT);
    APPSPAWN_CHECK(pfFds != NULL, ProcessExit(0), "prefork fds not found in queue");

    // Transfer fd ownership from spawningFdsQueue to forkCtx for subsequent spawning
    property->forkCtx.fd[0] = pfFds->fds[0];
    property->forkCtx.fd[1] = pfFds->fds[1];

    // Remove from queue without closing fd (ownership transferred to forkCtx)
    RemoveSpawningFdsByPid(mgr, getpid(), TYPE_CHILD_PARENT);

    // Read app spawn message from shared memory mmap
    property->state = APP_STATE_SPAWNING;
    const uint32_t memSize = (preforkMsg->msgLen / MAX_MSG_BLOCK_LEN + 1) * MAX_MSG_BLOCK_LEN;
    if (GetAppSpawnMsg(property, memSize, content->mode) == -1) {
        APPSPAWN_LOGE("prefork child read GetAppSpawnMsg failed");
        ClearPreforkInfo(property);
        content->notifyResToParent(content, &property->client, APPSPAWN_MSG_INVALID);
        ProcessExit(0);
    }
    // Spawn the actual app process. ProcessExit ensures the prefork child exits
    // with the return code from AppSpawnChild.
    ClearPreforkInfo(property);
    ProcessExit(AppSpawnChild(content, &property->client));
}

/**
 * @brief Creates two pipes before fork:
 * - childToParentFd: used for subsequent fork result notification (child -> parent)
 * - parentToChildFd: used for sending fork requests (parent -> prefork child)
 * Registers pipe fds to spawningFdsQueue BEFORE fork (with pid=-1 as placeholder),
 * then updates the pid after fork. This simplifies error handling since pre-fork
 * failures don't require killing a child process.
 *
 * @param mgr             AppSpawn manager instance
 * @param property        Current spawning context
 * @param childToParentFd       [out] Created prefork pipe (fd[0]=read, fd[1]=write)
 * @param parentToChildFd [out] Created parent-to-child pipe
 * @return fork result: >0 parent (child pid), 0 child, <0 fork failed
 */
APPSPAWN_STATIC pid_t ForkAndRegisterFds(AppSpawnMgr *mgr, AppSpawningCtx *property,
    int childToParentFd[PIPE_FD_LENGTH], int parentToChildFd[PIPE_FD_LENGTH])
{
    // 1. Create prefork pipe (child -> parent, for fork result notification)
    APPSPAWN_CHECK(pipe(childToParentFd) == 0, return -1,
        "prefork with prefork pipe failed %{public}d", errno);

    // 2. Create parent-to-child pipe (parent -> child, for sending fork requests)
    APPSPAWN_CHECK(pipe(parentToChildFd) == 0, ClearPipeFd(childToParentFd, PIPE_FD_LENGTH);
        return -1, "prefork with parent-child pipe failed %{public}d", errno);

    // 3. Register fds BEFORE fork (pid = -1 as placeholder)
    SpawningFdRegInfo pfRegInfo = { TYPE_CHILD_PARENT, PIPE_FD_LENGTH, childToParentFd, -1 };
    AppSpawnFds *pfFds = RegisterSpawningFds(mgr, &pfRegInfo);
    APPSPAWN_CHECK(pfFds != NULL, ClearPipeFd(childToParentFd, PIPE_FD_LENGTH);
        ClearPipeFd(parentToChildFd, PIPE_FD_LENGTH);
        return -1, "regist pfFds failed");

    SpawningFdRegInfo pcRegInfo = { TYPE_PARENT_CHILD, PIPE_FD_LENGTH, parentToChildFd, -1 };
    AppSpawnFds *pcFds = RegisterSpawningFds(mgr, &pcRegInfo);
    APPSPAWN_CHECK(pcFds != NULL, DeleteSpawningFds(&pfFds);
        ClearPipeFd(childToParentFd, PIPE_FD_LENGTH);
        ClearPipeFd(parentToChildFd, PIPE_FD_LENGTH);
        return -1, "regist pcFds failed");

    // 4. Fork prefork child process
    StartAppspawnTrace("AppspawnPreFork");
    pid_t pid = fork();
    if (pid > 0) {
        // Parent: update pid to child pid
        pfFds->pid = pid;
        pcFds->pid = pid;
        APPSPAWN_LOGV("create pipefd %{public}d %{public}d,%{public}d %{public}d,%{public}d",
            pid, childToParentFd[0], childToParentFd[1], parentToChildFd[0], parentToChildFd[1]);
        FinishAppspawnTrace();
    } else if (pid == 0) {
        HilogCloseSocketFd();
        // Child: update pid to own pid
        pfFds->pid = getpid();
        pcFds->pid = getpid();
    } else {
        // Fork failed: delete nodes (fd not closed) + close pipes
        DeleteSpawningFds(&pfFds);
        DeleteSpawningFds(&pcFds);
        ClearPipeFd(childToParentFd, PIPE_FD_LENGTH);
        ClearPipeFd(parentToChildFd, PIPE_FD_LENGTH);
        APPSPAWN_LOGE("prefork fork failed err %{public}d", errno);
        FinishAppspawnTrace();
    }
    return pid;
}

/**
 * @brief Prefork child process main loop: wait for pipe message and dispatch.
 *
 * This function runs in the prefork child after fork(). It:
 * 1. Clears the inherited forkCtx fds (not needed in prefork child)
 * 2. Sets process name to "PreforkProcess" for debugging
 * 3. Blocks on reading parentToChildFd[0] for a pipe message from parent
 * 4. Dispatches to the appropriate handler based on message type
 *
 * The prefork child is a long-lived process: it blocks on read() and handles
 * one fork request at a time. After handling, it calls ProcessExit() because
 * AppSpawnChild reuses the prefork child's pid for the actual app process.
 *
 * @param content     AppSpawn content
 * @param property    Spawning context
 * @param mgr         AppSpawn manager
 * @param errorLevel  fdsan error level saved before fork (to inherit in child)
 */
APPSPAWN_STATIC void PreforkChildLoop(AppSpawnContent *content, AppSpawningCtx *property,
    AppSpawnMgr *mgr, enum fdsan_error_level errorLevel)
{
    // Clear inherited forkCtx fds - they belong to the parent process
    ClearPipeFd(property->forkCtx.fd, PIPE_FD_LENGTH);

    // Set process name for debugging (visible in ps/top)
    int isRet = SetPreforkProcessName(content);
    APPSPAWN_LOGV("prefork process start wait read msg with set processname %{public}d", isRet);

    // Get parent-to-child pipe fd from spawningFdsQueue
    AppSpawnFds *pcFds = FindSpawningFdsByPid(mgr, getpid(), TYPE_PARENT_CHILD);
    APPSPAWN_CHECK(pcFds != NULL, ProcessExit(0), "parent child fds not found");

    // Block until parent sends a message (or pipe is closed)
    AppSpawnPipeMsg pipeMsg = {0};
    int infoSize = read(pcFds->fds[0], &pipeMsg, sizeof(AppSpawnPipeMsg));
    if (infoSize != sizeof(AppSpawnPipeMsg)) {
        APPSPAWN_LOGE("prefork process read msg failed %{public}d,%{public}d", infoSize, errno);
        ProcessExit(0);
    }

    // Inherit the fdsan error level of the parent process before doing any fd operations
    (void)fdsan_set_error_level(errorLevel);

    // Dispatch based on message type. Currently only MSG_APP_SPAWN is supported.
    // AppSpawnPipeMsg uses a type+union design for future extensibility.
    switch (pipeMsg.type) {
        case MSG_LOCK_STATUS:
            HandlePreforkUnlockMsg(content, &pipeMsg.msg.unlockMsg);
            break;
        case MSG_APP_SPAWN:
            HandlePreforkForkMsg(content, property, &pipeMsg.msg.preforkMsg);
            break;
        default:
            APPSPAWN_LOGE("prefork process received unknown msg type %{public}d", pipeMsg.type);
            ProcessExit(0);
    }
}

/**
 * @brief Fork a prefork child process and dispatch based on fork result.
 *
 * Called by AppSpawnProcessMsgForPrefork to maintain the prefork pool.
 * - On first call (reservedPid <= 0): creates the prefork child
 * - On subsequent calls: the prefork child already exists, this is a no-op
 *   (ForkAndRegisterFds will return -1 and be ignored)
 *
 * Pipe fd lifecycle:
 * - ForkAndRegisterFds creates pipes and registers them to spawningFdsQueue
 * - Parent side: holds fds in spawningFdsQueue, transfers to forkCtx in
 *   TransferPreforkFdToForkCtx when sending a fork request
 * - Child side: PreforkChildLoop reads from parentToChildFd, then
 *   HandlePreforkForkMsg takes over childToParentFd for app spawning
 */
static void ProcessPreFork(AppSpawnContent *content, AppSpawningCtx *property)
{
    AppSpawnMgr *mgr = (AppSpawnMgr *)content;

    content->reservedPid = -1;
    int childToParentFd[PIPE_FD_LENGTH] = {-1, -1};
    int parentToChildFd[PIPE_FD_LENGTH] = {-1, -1};
    enum fdsan_error_level errorLevel = fdsan_get_error_level();

    content->reservedPid = ForkAndRegisterFds(mgr, property, childToParentFd, parentToChildFd);
    // Child process: enter the prefork wait loop (never returns)
    APPSPAWN_ONLY_EXPER(content->reservedPid == 0, PreforkChildLoop(content, property, mgr, errorLevel));
}

static int NormalSpawnChild(AppSpawnContent *content, AppSpawnClient *client, pid_t *childPid)
{
    APPSPAWN_CHECK(client != NULL, return APPSPAWN_ARG_INVALID, "client is null");
    int ret = InitForkContext((AppSpawningCtx *)client);
    APPSPAWN_CHECK(ret == 0, return ret, "init fork context failed");
    return AppSpawnProcessMsg(content, client, childPid);
}

/**
 * @brief Prepare prefork message: allocate mmap, write msg via shared memory, build pipe message.
 *
 * Three-step preparation:
 * 1. Allocate shared memory (mmap) for the app spawn message
 * 2. Write the message to shared memory via WritePreforkMsg
 * 3. Build a lightweight AppSpawnPipeMsg containing only client id, flags, and msgLen
 *    (the prefork child will read the full message from mmap using these hints)
 *
 * @param content     AppSpawn content
 * @param property    Spawning context containing the full message
 * @param client      Client info (id, flags)
 * @param memSize     Shared memory size (already aligned to MAX_MSG_BLOCK_LEN)
 * @param outPipeMsg  [out] Allocated pipe message (caller must free)
 * @return APPSPAWN_OK on success, APPSPAWN_SYSTEM_ERROR on failure
 */
APPSPAWN_STATIC int PreparePreforkMsg(AppSpawnContent *content, AppSpawningCtx *property,
    const AppSpawnClient *client, uint32_t memSize, AppSpawnPipeMsg **outPipeMsg)
{
    // Step 1: Allocate shared memory for the app spawn message.
    // The prefork child will read from this mmap instead of receiving via socket.
    content->propertyBuffer = GetMapMem(property->client.id, "prefork", memSize, false, content->mode);
    APPSPAWN_ONLY_EXPER(content->propertyBuffer == NULL, ClearMMAP(property->client.id, memSize);
        return APPSPAWN_SYSTEM_ERROR);
    // Step 2: Copy the message from property->message to the shared memory
    int ret = WritePreforkMsg(property, memSize);
    APPSPAWN_ONLY_EXPER(ret != 0, ClearMMAP(property->client.id, memSize);
        return APPSPAWN_SYSTEM_ERROR);
    // Step 3: Build a lightweight pipe message to signal the prefork child.
    // Only sends metadata (id, flags, msgLen); the child reads full data from mmap.
    AppSpawnPipeMsg *pipeMsg = (AppSpawnPipeMsg *)calloc(1, sizeof(AppSpawnPipeMsg));
    APPSPAWN_ONLY_EXPER(pipeMsg == NULL, APPSPAWN_LOGE("calloc failed");
        ClearMMAP(property->client.id, memSize);
        return APPSPAWN_SYSTEM_ERROR);

    pipeMsg->type = MSG_APP_SPAWN;
    pipeMsg->msg.preforkMsg.id = client->id;
    pipeMsg->msg.preforkMsg.flags = client->flags;
    pipeMsg->msg.preforkMsg.msgLen = property->message->msgHeader.msgLen;
    *outPipeMsg = pipeMsg;
    return APPSPAWN_OK;
}

/**
 * @brief Send pipe message to prefork child process.
 *
 * Writes the AppSpawnPipeMsg to parentToChildFd[1] (write end of the pipe).
 * On success, immediately closes and unregisters the parent-child pipe fds
 * since they are no longer needed after the message is sent.
 *
 * @param mgr       AppSpawn manager instance
 * @param childPid  Prefork child process ID
 * @param pipeMsg   Pipe message to send
 * @return APPSPAWN_OK on success, APPSPAWN_ARG_INVALID if fd not found, APPSPAWN_SYSTEM_ERROR on write failure
 */
APPSPAWN_STATIC int SendPipeMsgToChild(AppSpawnMgr *mgr, pid_t childPid, AppSpawnPipeMsg *pipeMsg)
{
    // Look up parent-to-child pipe from spawningFdsQueue
    AppSpawnFds *pcFds = FindSpawningFdsByPid(mgr, childPid, TYPE_PARENT_CHILD);
    if (pcFds == NULL) {
        APPSPAWN_LOGE("parent child fds not found for pid %{public}d", childPid);
        return APPSPAWN_ARG_INVALID;
    }

    // Write the pipe message (blocks if pipe buffer is full, which should not happen
    // since sizeof(AppSpawnPipeMsg) is small and child is blocking on read)
    ssize_t writesize = write(pcFds->fds[1], pipeMsg, sizeof(AppSpawnPipeMsg));
    if (writesize < 0 || (size_t)writesize != sizeof(AppSpawnPipeMsg)) {
        APPSPAWN_LOGE("write msg to child failed %{public}d", errno);
        return APPSPAWN_SYSTEM_ERROR;
    }

    // Close parent's pipe fds immediately after write succeeds.
    // The child has already read the data, so parent no longer needs these fds.
    UnregisterSpawningFdsByPid(mgr, childPid, TYPE_PARENT_CHILD);
    return APPSPAWN_OK;
}

/**
 * @brief Cleanup prefork child on failure: kill child, cleanup spawning fds.
 *
 * Called when sending a fork request to the prefork child fails.
 * Kills the prefork child and removes all its fds from spawningFdsQueue.
 * The next spawn request will create a new prefork child via ProcessPreFork.
 */
APPSPAWN_STATIC void CleanupPreforkChild(AppSpawnMgr *mgr, pid_t childPid)
{
    (void)kill(childPid, SIGKILL);
    // Remove all fds (TYPE_CHILD_PARENT + TYPE_PARENT_CHILD) for this child
    CleanupSpawningFdsByPid(mgr, childPid);
}

/**
 * @brief Transfer childToParentFd from spawningFdsQueue to forkCtx.
 *
 * After the prefork child has been notified via pipe, the parent needs to
 * take over the prefork pipe for result notification. This function:
 * 1. Looks up TYPE_CHILD_PARENT fds from the queue
 * 2. Copies fd values to property->forkCtx.fd
 * 3. Removes the node from queue WITHOUT closing fds (ownership transfer)
 * 4. Sets the read end to non-blocking mode
 *
 * @return APPSPAWN_OK on success, APPSPAWN_ARG_INVALID if prefork fds not found
 */
APPSPAWN_STATIC int TransferPreforkFdToForkCtx(AppSpawnMgr *mgr, pid_t childPid, AppSpawningCtx *property)
{
    AppSpawnFds *pfFds = FindSpawningFdsByPid(mgr, childPid, TYPE_CHILD_PARENT);
    if (pfFds == NULL) {
        APPSPAWN_LOGE("prefork fds not found for pid %{public}d", childPid);
        return APPSPAWN_ARG_INVALID;
    }

    // Copy fd values to forkCtx (fd ownership transfers, do not close)
    property->forkCtx.fd[0] = pfFds->fds[0];
    property->forkCtx.fd[1] = pfFds->fds[1];
    // Remove from queue without closing fd (ownership transferred to forkCtx)
    RemoveSpawningFdsByPid(mgr, childPid, TYPE_CHILD_PARENT);

    // Set read end to non-blocking (F_GETFL/F_SETFL for file status flags)
    int flags = fcntl(property->forkCtx.fd[0], F_GETFL);
    if (flags >= 0) {
        int ret = fcntl(property->forkCtx.fd[0], F_SETFL, (unsigned int)flags | O_NONBLOCK);
        APPSPAWN_CHECK_ONLY_LOG(ret == 0, "fcntl failed %{public}d,%{public}d", ret, errno);
    }
    return APPSPAWN_OK;
}

/**
 * @brief Process app spawn message using prefork optimization.
 *
 * Prefork flow:
 * 1. If no prefork child exists (reservedPid <= 0):
 *    - Fall back to normal fork for this request
 *    - Fork a new prefork child for future requests (ProcessPreFork)
 *
 * 2. If prefork child exists (reservedPid > 0):
 *    - Prepare message in shared memory (PreparePreforkMsg)
 *    - Send pipe message to prefork child (SendPipeMsgToChild)
 *    - Take over prefork pipe for result notification (TransferPreforkFdToForkCtx)
 *    - On failure: kill prefork child, cleanup fds, fork a new one (ProcessPreFork)
 *    - On success: fork a new prefork child for next request (ProcessPreFork)
 *
 * @return APPSPAWN_OK on success, APPSPAWN_SYSTEM_ERROR on failure
 */
static int AppSpawnProcessMsgForPrefork(AppSpawnContent *content, AppSpawnClient *client, pid_t *childPid)
{
    int ret = APPSPAWN_OK;
    AppSpawningCtx *property = (AppSpawningCtx *)client;
    AppSpawnMgr *mgr = (AppSpawnMgr *)content;

    if (content->reservedPid <= 0) {
        // No prefork child available: use normal fork for this request,
        // then create a new prefork child for future requests
        ret = NormalSpawnChild(content, client, childPid);
        ProcessPreFork(content, property);
        return ret;
    }

    // Prefork child exists: prepare to send fork request
    const uint32_t memSize = (property->message->msgHeader.msgLen / MAX_MSG_BLOCK_LEN + 1) *
        MAX_MSG_BLOCK_LEN;
    AppSpawnPipeMsg *pipeMsg = NULL;
    // Failed to prepare shared memory: fallback to normal fork
    APPSPAWN_ONLY_EXPER(PreparePreforkMsg(content, property, client, memSize, &pipeMsg) != APPSPAWN_OK,
        return NormalSpawnChild(content, client, childPid));
    *childPid = content->reservedPid;

    // Send pipe message and transfer prefork fd. If either fails,
    // kill the prefork child and cleanup its fds.
    if (SendPipeMsgToChild(mgr, *childPid, pipeMsg) != APPSPAWN_OK ||
        TransferPreforkFdToForkCtx(mgr, *childPid, property) != APPSPAWN_OK) {
        CleanupPreforkChild(mgr, *childPid);
        *childPid = 0;
        ret = APPSPAWN_SYSTEM_ERROR;
    }

    free(pipeMsg);
    // Always try to create a new prefork child for the next request,
    // regardless of whether this request succeeded or failed
    ProcessPreFork(content, property);
    return ret;
}

static bool IsSupportPrefork(AppSpawnContent *content, AppSpawnClient *client)
{
#ifdef APPSPAWN_SUPPORT_PREFORK
    if (client == NULL || content == NULL) {
        return false;
    }
    if (!content->enablePerfork) {
        APPSPAWN_LOGV("g_enablePrefork %{public}d", content->enablePerfork);
        return false;
    }
    AppSpawningCtx *property = (AppSpawningCtx *)client;

    if (content->mode == MODE_FOR_APP_SPAWN && !IsChildColdRun(property)
        && !CheckAppMsgFlagsSet(property, APP_FLAGS_CHILDPROCESS)) {
        return true;
    }
#endif
    return false;
}

static bool IsBootFinished(void)
{
    char buffer[32] = {0};  // 32 max
    int ret = GetParameter("bootevent.boot.completed", "false", buffer, sizeof(buffer));
    bool isBootCompleted = (ret > 0 && strcmp(buffer, "true") == 0);
    return isBootCompleted;
}


static int RunAppSpawnProcessMsg(AppSpawnContent *content, AppSpawnClient *client, pid_t *childPid)
{
    int ret = 0;

    if (IsBootFinished() && IsSupportPrefork(content, client)) {
        ret = AppSpawnProcessMsgForPrefork(content, client, childPid);
    } else {
        ret = NormalSpawnChild(content, client, childPid);
    }
    return ret;
}

static void ProcessSpawnReqMsg(AppSpawnConnection *connection, AppSpawnMsgNode *message)
{
    int ret = CheckAppSpawnMsg(message);
    if (ret != 0) {
        SendResponse(connection, &message->msgHeader, ret, 0);
        DeleteAppSpawnMsg(&message);
        return;
    }

    if (IsDeveloperModeOpen()) {
        if (IsSupportRunHnp()) {
            SetAppSpawnMsgFlag(message, TLV_MSG_FLAGS, APP_FLAGS_DEVELOPER_MODE);
        } else {
            APPSPAWN_LOGV("Not support execute hnp file!");
        }
    }

    AppSpawningCtx *property = CreateAppSpawningCtx();
    if (property == NULL) {
        SendResponse(connection, &message->msgHeader, APPSPAWN_SYSTEM_ERROR, 0);
        DeleteAppSpawnMsg(&message);
        return;
    }

    property->state = APP_STATE_SPAWNING;
    property->message = message;
    message->connection = connection;

    StartAppspawnTrace("STAGE_PARENT_MSG_DECODE");
    ret = AppSpawnHookExecute(STAGE_PARENT_MSG_DECODE, HOOK_STOP_WHEN_ERROR, GetAppSpawnContent(), &property->client);
    FinishAppspawnTrace();
    // Check if SPM message rebuild hook failed
    APPSPAWN_ONLY_EXPER(ret != 0, APPSPAWN_LOGE("rebuild hook failed: %{public}d, aborting spawn", ret);
        SendResponse(connection, &message->msgHeader, ret, 0);
        DeleteAppSpawningCtx(property);
        return);

    // mount el2 dir
    // getWrapBundleNameValue
    AppSpawnHookExecute(STAGE_PARENT_PRE_FORK, 0, GetAppSpawnContent(), &property->client);
    DumpAppSpawnMsg(property->message);

    clock_gettime(CLOCK_MONOTONIC, &property->spawnStart);
    ret = RunAppSpawnProcessMsg(GetAppSpawnContent(), &property->client, &property->pid);
    AppSpawnHookExecute(STAGE_PARENT_POST_FORK, 0, GetAppSpawnContent(), &property->client);
    if (ret != 0) { // wait child process result
        AppSpawnHookExecute(STAGE_SERVER_SPAWN_ABORT, 0, GetAppSpawnContent(), &property->client);
        SendResponse(connection, &message->msgHeader, ret, 0);
        DeleteAppSpawningCtx(property);
        return;
    }
    if (AddChildWatcher(property) != 0) { // wait child process result
        kill(property->pid, SIGKILL);
        AppSpawnHookExecute(STAGE_SERVER_SPAWN_ABORT, 0, GetAppSpawnContent(), &property->client);
        SendResponse(connection, &message->msgHeader, ret, 0);
        DeleteAppSpawningCtx(property);
        return;
    }
}

static uint32_t g_lastDiedAppId = 0;
static uint32_t g_crashTimes = 0;
#define MAX_CRASH_TIME 5
static void WaitChildDied(pid_t pid, int status)
{
    AppSpawningCtx *property = GetAppSpawningCtxByPid(pid);
    if (property != NULL && property->message != NULL && property->state == APP_STATE_SPAWNING) {
        const char *processName = GetProcessName(property);
        APPSPAWN_LOGI("Child process %{public}s fail \'child crash \'pid %{public}d appId: %{public}d",
            processName, property->pid, property->client.id);
#ifdef APPSPAWN_HISYSEVENT
        ReportSpawnChildProcessFail(processName, ERR_APPSPAWN_CHILD_CRASH, APPSPAWN_CHILD_CRASH);
#endif
        if (WIFSIGNALED(status)) {
            if (property->client.id == g_lastDiedAppId + 1) {
                g_crashTimes++;
            } else {
                g_crashTimes = 1;
            }
            g_lastDiedAppId = property->client.id;
        } else {
            g_crashTimes = 0;
        }

        SendResponse(property->message->connection, &property->message->msgHeader, APPSPAWN_CHILD_CRASH, 0);
        AppSpawnHookExecute(STAGE_SERVER_SPAWN_ABORT, 0, GetAppSpawnContent(), &property->client);
        DeleteAppSpawningCtx(property);

        if (g_crashTimes >= MAX_CRASH_TIME) {
            APPSPAWN_LOGW("Continuous failures in spawning the app, restart appspawn");
#ifdef APPSPAWN_HISYSEVENT
            ReportKeyEvent(APPSPAWN_MAX_FAILURES_EXCEEDED);
#endif
            StopAppSpawn();
        }
    }
}

static void WaitChildTimeout(const TimerHandle taskHandle, void *context)
{
    AppSpawningCtx *property = (AppSpawningCtx *)context;
    APPSPAWN_LOGI("Child process %{public}s fail \'wait child timeout \'pid %{public}d appId: %{public}d",
        GetProcessName(property), property->pid, property->client.id);
    if (property->pid > 0) {
#if (!defined(CJAPP_SPAWN) && !defined(NATIVE_SPAWN))
        DumpSpawnStack(property->pid);
#endif
        kill(property->pid, SIGKILL);
    }
#ifdef APPSPAWN_HISYSEVENT
    ReportSpawnChildProcessFail(GetProcessName(property), ERR_APPSPAWN_SPAWN_TIMEOUT, APPSPAWN_SPAWN_TIMEOUT);
#endif
    SendResponse(property->message->connection, &property->message->msgHeader, APPSPAWN_SPAWN_TIMEOUT, 0);
    AppSpawnHookExecute(STAGE_SERVER_SPAWN_ABORT, 0, GetAppSpawnContent(), &property->client);
    DeleteAppSpawningCtx(property);
}

static int ProcessChildFdCheck(int fd, AppSpawningCtx *property)
{
    int result = 0;
    (void)read(fd, &result, sizeof(result));
    APPSPAWN_DUMPI("Child process:%{public}s success pid:%{public}d appId:%{public}d result:%{public}d",
        GetProcessName(property), property->pid, property->client.id, result);
    APPSPAWN_CHECK(property->message != NULL, return -1, "Invalid message in ctx %{public}d", property->client.id);

    if (result != 0) {
#ifdef APPSPAWN_HISYSEVENT
        ReportSpawnChildProcessFail(GetProcessName(property), ERR_APPSPAWN_SPAWN_FAIL, result);
#endif
        AppSpawnHookExecute(STAGE_SERVER_SPAWN_ABORT, 0, GetAppSpawnContent(), &property->client);
        SendResponse(property->message->connection, &property->message->msgHeader, result, property->pid);
        DeleteAppSpawningCtx(property);
        return -1;
    }

    return 0;
}

static uint32_t GetAppCloneIndex(AppSpawningCtx *property)
{
    APPSPAWN_ONLY_EXPER(!CheckAppMsgFlagsSet(property, APP_FLAGS_CLONE_ENABLE), return 0);
    AppSpawnMsgBundleInfo *bundleInfo = (AppSpawnMsgBundleInfo *)GetAppProperty(property, TLV_BUNDLE_INFO);
    return (bundleInfo != NULL) ? bundleInfo->bundleIndex : 0;
}

static AppSpawnedProcess *InitSpawnedProcessInfo(AppSpawningCtx *property)
{
    bool isDebuggable = CheckAppMsgFlagsSet(property, APP_FLAGS_DEBUGGABLE);
    uint32_t appIndex = GetAppCloneIndex(property);

    // Get tokenid from ACCESS_TOKEN_INFO TLV
    const AppSpawnMsgAccessToken *tokenInfo = (const AppSpawnMsgAccessToken *)
        GetAppSpawnMsgInfo(property->message, TLV_ACCESS_TOKEN_INFO);
    uint64_t tokenid = (tokenInfo != NULL) ? tokenInfo->accessTokenIdEx : 0;

    AppSpawnedProcess *appInfo = AddSpawnedProcess(property->pid, GetBundleName(property),
        appIndex, isDebuggable, tokenid);
    APPSPAWN_CHECK(appInfo != NULL, return NULL, "Failed to add spawned process");

    AppSpawnMsgDacInfo *dacInfo = GetAppProperty(property, TLV_DAC_INFO);
    appInfo->uid = (dacInfo != NULL) ? dacInfo->uid : 0;
    appInfo->spawnStart.tv_sec = property->spawnStart.tv_sec;
    appInfo->spawnStart.tv_nsec = property->spawnStart.tv_nsec;
    appInfo->lockBundleRefAdded = property->lockBundleRefAdded;  // Copy flag from AppSpawningCtx
    appInfo->spmRefAdded = property->spmRefAdded;  // Copy SPM refcount flag
    appInfo->lockPath = property->lockPath;  // Transfer lockPath ownership to AppSpawnedProcess
    property->lockPath = NULL;  // Prevent double free

    return appInfo;
}

static void HandleSpawnProcessInfoFail(AppSpawningCtx *property)
{
    APPSPAWN_LOGE("Failed to init spawned process info, killing pid %{public}d", property->pid);
    kill(property->pid, SIGKILL);
#ifdef APPSPAWN_HISYSEVENT
    ReportSpawnChildProcessFail(GetProcessName(property), ERR_APPSPAWN_PROCESS_INFO_INIT_FAIL,
        ERR_APPSPAWN_PROCESS_INFO_INIT_FAIL);
#endif
    AppSpawnHookExecute(STAGE_SERVER_SPAWN_ABORT, 0, GetAppSpawnContent(), &property->client);
    SendResponse(property->message->connection, &property->message->msgHeader,
        APPSPAWN_SYSTEM_ERROR, property->pid);
    DeleteAppSpawningCtx(property);
}

static void ProcessChildResponse(const WatcherHandle taskHandle, int fd, uint32_t *events, const void *context)
{
    AppSpawningCtx *property = (AppSpawningCtx *)context;
    property->forkCtx.watcherHandle = NULL;  // delete watcher
    LE_RemoveWatcher(LE_GetDefaultLoop(), (WatcherHandle)taskHandle);

    if (ProcessChildFdCheck(fd, property) != 0) {
        return;
    }

    // success
    AppSpawnedProcess *appInfo = InitSpawnedProcessInfo(property);
    APPSPAWN_ONLY_EXPER(appInfo == NULL, HandleSpawnProcessInfoFail(property);
        return);

#ifdef DEBUG_BEGETCTL_BOOT
    if (IsDeveloperModeOpen()) {
        appInfo->message = property->message;
    }
#endif
    clock_gettime(CLOCK_MONOTONIC, &appInfo->spawnEnd);

#ifdef APPSPAWN_HISYSEVENT
    //add process spawn duration into hisysevent,(ms)
    uint32_t spawnProcessDuration = (uint32_t)((appInfo->spawnEnd.tv_sec - appInfo->spawnStart.tv_sec) *
        (APPSPAWN_USEC_TO_NSEC)) +(uint32_t)((appInfo->spawnEnd.tv_nsec - appInfo->spawnStart.tv_nsec) /
        (APPSPAWN_MSEC_TO_NSEC));
    AppSpawnMgr *appspawnMgr = GetAppSpawnMgr();
    if (appspawnMgr != NULL) {
        AddStatisticEventInfo(appspawnMgr->hisyseventInfo, spawnProcessDuration, IsBootFinished());
    }
#ifndef ASAN_DETECTOR
    uint64_t diff = DiffTime(&appInfo->spawnStart, &appInfo->spawnEnd);
    APPSPAWN_CHECK_ONLY_EXPER(diff < (IsChildColdRun(property) ? SPAWN_COLDRUN_DURATION : SPAWN_DURATION),
        ReportAbnormalDuration("SPAWNCHILD", diff));
#endif
#endif

    WatchChildProcessFd(property);
    ProcessMgrHookExecute(STAGE_SERVER_APP_ADD, GetAppSpawnContent(), appInfo);
    // response
    AppSpawnHookExecute(STAGE_PARENT_PRE_RELY, 0, GetAppSpawnContent(), &property->client);
    StartAppspawnTrace("SendResponse");
    SendResponse(property->message->connection, &property->message->msgHeader, 0, property->pid);
    FinishAppspawnTrace();
    AppSpawnHookExecute(STAGE_PARENT_POST_RELY, 0, GetAppSpawnContent(), &property->client);
#ifdef DEBUG_BEGETCTL_BOOT
    if (IsDeveloperModeOpen()) {
        property->message = NULL;
    }
#endif
    DeleteAppSpawningCtx(property);
}

static void NotifyResToParent(AppSpawnContent *content, AppSpawnClient *client, int result)
{
    AppSpawningCtx *property = (AppSpawningCtx *)client;
    APPSPAWN_CHECK(property != NULL && client != NULL, return, "invalid param");
    int fd = property->forkCtx.fd[1];
    if (fd >= 0) {
        (void)write(fd, &result, sizeof(result));
        (void)close(fd);
        property->forkCtx.fd[1] = -1;
    }
    APPSPAWN_LOGV("NotifyResToParent client id: %{public}u result: 0x%{public}x", client->id, result);
}

static int CreateAppSpawnServer(TaskHandle *server, const char *socketName)
{
    char path[128] = {0};  // 128 max path
    int ret = snprintf_s(path, sizeof(path), sizeof(path) - 1, "%s%s", APPSPAWN_SOCKET_DIR, socketName);
    APPSPAWN_CHECK(ret > 0, return -1, "Failed to snprintf_s %{public}d", ret);
    int socketId = GetControlSocket(socketName);
    APPSPAWN_LOGI("get socket form env %{public}s socketId %{public}d", socketName, socketId);

    LE_StreamServerInfo info = {};
    info.baseInfo.flags = TASK_STREAM | TASK_PIPE | TASK_SERVER;
    info.socketId = socketId;
    info.server = path;
    info.baseInfo.close = OnListenFdClose;
    info.incommingConnect = OnConnection;

    MakeDirRec(path, 0711, 0);  // 0711 default mask
    ret = LE_CreateStreamServer(LE_GetDefaultLoop(), server, &info);
    APPSPAWN_CHECK(ret == 0, return -1, "Failed to create socket for %{public}s errno: %{public}d", path, errno);
    SetFdCtrl(LE_GetSocketFd(*server), FD_CLOEXEC);

    APPSPAWN_LOGI("CreateAppSpawnServer path %{public}s fd %{public}d", path, LE_GetSocketFd(*server));
    return 0;
}

void AppSpawnDestroyContent(AppSpawnContent *content)
{
    if (content == NULL) {
        return;
    }
    AppSpawnMgr *appSpawnContent = (AppSpawnMgr *)content;
    if (appSpawnContent->sigHandler != NULL) {
        // LE_CloseSignalTask removes signalfd from epoll via epoll_ctl(EPOLL_CTL_DEL).
        // Only parent process can call this, as child process would affect parent's epoll.
        // After fork, child has duplicated signalfd and must close it directly.
        APPSPAWN_ONLY_EXPER(appSpawnContent->servicePid == getpid(),
            LE_CloseSignalTask(LE_GetDefaultLoop(), appSpawnContent->sigHandler);
            appSpawnContent->sigHandler = NULL);
        int fd = LE_GetSocketFd(appSpawnContent->sigHandler);
        APPSPAWN_ONLY_EXPER(appSpawnContent->servicePid != getpid() && fd > 0, close(fd));
    }

    // Close the observe process signal fd used for writing child process death info.
    APPSPAWN_ONLY_EXPER(content->signalFd > 0, close(content->signalFd);
        content->signalFd = -1);
    if (appSpawnContent->server != NULL && appSpawnContent->servicePid == getpid()) { // childProcess can't deal socket
        LE_CloseStreamTask(LE_GetDefaultLoop(), appSpawnContent->server);
        appSpawnContent->server = NULL;
    }
    DeleteAppSpawnMgr(appSpawnContent);
    LE_StopLoop(LE_GetDefaultLoop());
    LE_CloseLoop(LE_GetDefaultLoop());
}

APPSPAWN_STATIC int AppSpawnColdStartApp(struct AppSpawnContent *content, AppSpawnClient *client)
{
    AppSpawnMgr *mgr = (AppSpawnMgr *)content;
    AppSpawningCtx *property = (AppSpawningCtx *)client;
    const char *processName = GetProcessName(property);
    APPSPAWN_CHECK(processName != NULL, return APPSPAWN_ARG_INVALID, "Failed to get process name");
    // for cold run, use shared memory to exchange message
    APPSPAWN_LOGV("Write msg to child %{public}s", processName);
    int ret = WriteMsgToChild(property, content->mode);
    APPSPAWN_CHECK(ret == 0, return APPSPAWN_SYSTEM_ERROR, "Failed to write msg to child");

#ifdef CJAPP_SPAWN
    char *path = property->forkCtx.coldRunPath != NULL ? property->forkCtx.coldRunPath : "/system/bin/cjappspawn";
#elif NATIVE_SPAWN
    char *path = property->forkCtx.coldRunPath != NULL ? property->forkCtx.coldRunPath : "/system/bin/nativespawn";
#elif HYBRID_SPAWN
    char *path = property->forkCtx.coldRunPath != NULL ? property->forkCtx.coldRunPath : "/system/bin/hybridspawn";
#elif NWEB_SPAWN
    char *path = property->forkCtx.coldRunPath != NULL ? property->forkCtx.coldRunPath : "/system/bin/nwebspawn";
#else
    char *path = property->forkCtx.coldRunPath != NULL ? property->forkCtx.coldRunPath : "/system/bin/appspawn";
#endif
    char buffer[4][32] = {0};  // 4 32 buffer for fd
    int len = sprintf_s(buffer[0], sizeof(buffer[0]), " %d ", property->forkCtx.fd[1]);
    APPSPAWN_CHECK(len > 0, return APPSPAWN_SYSTEM_ERROR, "Invalid to format fd");
    len = sprintf_s(buffer[1], sizeof(buffer[1]), " %u ", property->client.flags);
    APPSPAWN_CHECK(len > 0, return APPSPAWN_SYSTEM_ERROR, "Invalid to format flags");
    len = sprintf_s(buffer[2], sizeof(buffer[2]), " %u ", property->forkCtx.msgSize); // 2 2 index for dest path
    APPSPAWN_CHECK(len > 0, return APPSPAWN_SYSTEM_ERROR, "Invalid to format msgSize");
    len = sprintf_s(buffer[3], sizeof(buffer[3]), " %u ", property->client.id); // 3 3 index for client id
    APPSPAWN_CHECK(len > 0, return APPSPAWN_SYSTEM_ERROR, "Invalid to format clientId");
    char *mode = IsAppSpawnMode(mgr) ? "app_cold" : (IsNWebSpawnMode(mgr) ? "nweb_cold" :
        (IsCJSpawnMode(mgr) ? "cj_app_cold" : (IsNativeSpawnMode(mgr) ? "native_cold" : "hybrid_cold")));
    APPSPAWN_LOGI("ColdStartApp::processName:%{public}s path:%{public}s mode:%{public}s", processName, path, mode);

#ifndef APPSPAWN_TEST
    const char *const formatCmds[] = {
        path, "-mode", mode, "-fd", buffer[0], buffer[1], buffer[2], "-param", processName, buffer[3], NULL
    };
    ret = execv(path, (char **)formatCmds);
    if (ret != 0) {
        APPSPAWN_LOGE("Failed to execv, errno: %{public}d", errno);
    }
#endif
    APPSPAWN_LOGV("ColdStartApp::processName:%{public}s end", processName);
    return 0;
}

static AppSpawningCtx *GetAppSpawningCtxFromArg(AppSpawnMgr *content, int argc, char *const argv[])
{
    AppSpawningCtx *property = CreateAppSpawningCtx();
    APPSPAWN_CHECK(property != NULL, return NULL, "Create app spawning ctx fail");
    property->forkCtx.fd[1] = atoi(argv[FD_VALUE_INDEX]);
    property->client.flags = (uint32_t)atoi(argv[FLAGS_VALUE_INDEX]);
    property->client.flags &= ~APP_COLD_START;

    uint32_t size = (uint32_t)atoi(argv[SHM_SIZE_INDEX]);
    property->client.id = (uint32_t)atoi(argv[CLIENT_ID_INDEX]);
    uint8_t *buffer = (uint8_t *)GetMapMem(property->client.id,
        argv[PARAM_VALUE_INDEX], size, true, content->content.mode);
    if (buffer == NULL) {
        APPSPAWN_LOGE("Failed to map errno %{public}d %{public}s", property->client.id, argv[PARAM_VALUE_INDEX]);
        NotifyResToParent(&content->content, &property->client, APPSPAWN_SYSTEM_ERROR);
        DeleteAppSpawningCtx(property);
        return NULL;
    }

    uint32_t msgRecvLen = 0;
    uint32_t remainLen = 0;
    AppSpawnMsgNode *message = NULL;
    int ret = GetAppSpawnMsgFromBuffer(buffer, ((AppSpawnMsg *)buffer)->msgLen, &message, &msgRecvLen, &remainLen);
    // release map
    munmap((char *)buffer, size);
    //unlink
    char path[PATH_MAX] = {0};
    int len = sprintf_s(path, sizeof(path), APPSPAWN_MSG_DIR "%s/%s_%u",
        GetSpawnNameByRunMode(content->content.mode), argv[PARAM_VALUE_INDEX], property->client.id);
    if (len > 0) {
        unlink(path);
    }

    if (ret == 0 && DecodeAppSpawnMsg(message) == 0 && CheckAppSpawnMsg(message) == 0) {
        property->message = message;
        message = NULL;
        return property;
    }
    NotifyResToParent(&content->content, &property->client, APPSPAWN_MSG_INVALID);
    DeleteAppSpawnMsg(&message);
    DeleteAppSpawningCtx(property);
    return NULL;
}

static void AppSpawnColdRun(AppSpawnContent *content, int argc, char *const argv[])
{
    APPSPAWN_CHECK(argc >= ARG_NULL, return, "Invalid arg for cold start %{public}d", argc);
    AppSpawnMgr *appSpawnContent = (AppSpawnMgr *)content;
    APPSPAWN_CHECK(appSpawnContent != NULL, return, "Invalid appspawn content");

    AppSpawningCtx *property = GetAppSpawningCtxFromArg(appSpawnContent, argc, argv);
    if (property == NULL) {
        APPSPAWN_LOGE("Failed to get property from arg");
        return;
    }
    DumpAppSpawnMsg(property->message);

    int ret = AppSpawnExecuteSpawningHook(content, &property->client);
    if (ret == 0) {
        ret = AppSpawnExecutePreReplyHook(content, &property->client);
        // success
        NotifyResToParent(content, &property->client, ret);

        (void)AppSpawnExecutePostReplyHook(content, &property->client);

        ret = APPSPAWN_SYSTEM_ERROR;
        if (content->runChildProcessor != NULL) {
            ret = content->runChildProcessor(content, &property->client);
        }
    } else {
        NotifyResToParent(content, &property->client, ret);
    }

    if (ret != 0) {
        AppSpawnEnvClear(content, &property->client);
    }
    APPSPAWN_LOGI("AppSpawnColdRun exit %{public}d.", getpid());
}

static void AppSpawnRun(AppSpawnContent *content, int argc, char *const argv[])
{
    APPSPAWN_LOGI("AppSpawnRun");
    AppSpawnMgr *appSpawnContent = (AppSpawnMgr *)content;
    APPSPAWN_CHECK(appSpawnContent != NULL, return, "Invalid appspawn content");

    LE_STATUS status = LE_CreateSignalTask(LE_GetDefaultLoop(), &appSpawnContent->sigHandler, ProcessSignal);
    if (status == 0) {
        (void)LE_AddSignal(LE_GetDefaultLoop(), appSpawnContent->sigHandler, SIGCHLD);
        (void)LE_AddSignal(LE_GetDefaultLoop(), appSpawnContent->sigHandler, SIGTERM);
    }

    if (IsAppSpawnMode(appSpawnContent) || IsHybridSpawnMode(appSpawnContent)) {
        struct sched_param param = { 0 };
        param.sched_priority = 1;
        int ret = sched_setscheduler(0, SCHED_FIFO, &param);
        APPSPAWN_CHECK_ONLY_LOG(ret == 0, "UpdateSchedPrio failed ret: %{public}d, %{public}d", ret, errno);
    }
    LE_RunLoop(LE_GetDefaultLoop());
    APPSPAWN_LOGI("AppSpawnRun exit mode: %{public}d ", content->mode);

    (void)ServerStageHookExecute(STAGE_SERVER_EXIT, content); // service exit,plugin can deal task
    AppSpawnDestroyContent(content);
}

APPSPAWN_STATIC int AppSpawnClearEnv(AppSpawnMgr *content, AppSpawningCtx *property)
{
    APPSPAWN_CHECK(content != NULL, return 0, "Invalid appspawn content");
    StartAppspawnTrace("AppSpawnClearEnv");
    DeleteAppSpawningCtx(property);
    AppSpawnDestroyContent(&content->content);
    APPSPAWN_LOGV("clear %{public}d end", getpid());
    FinishAppspawnTrace();
    return 0;
}

static int IsEnablePrefork(void)
{
    char buffer[32] = {0};
    int ret = GetParameter("persist.sys.prefork.enable", "true", buffer, sizeof(buffer));
    APPSPAWN_LOGV("IsEnablePrefork result %{public}d, %{public}s", ret, buffer);
    return strcmp(buffer, "true") == 0;
}

AppSpawnContent *AppSpawnCreateContent(const char *socketName, char *longProcName, uint32_t nameLen, int mode)
{
    APPSPAWN_CHECK(socketName != NULL && longProcName != NULL, return NULL, "Invalid name");
    APPSPAWN_LOGI("AppSpawnCreateContent %{public}s %{public}u mode %{public}d", socketName, nameLen, mode);

    AppSpawnMgr *appSpawnContent = CreateAppSpawnMgr(mode);
    APPSPAWN_CHECK(appSpawnContent != NULL, return NULL, "Failed to alloc memory for appspawn");
#ifdef APPSPAWN_HISYSEVENT
    appSpawnContent->hisyseventInfo = InitHisyseventTimer();
#endif
    appSpawnContent->content.longProcName = longProcName;
    appSpawnContent->content.longProcNameLen = nameLen;
    appSpawnContent->content.notifyResToParent = NotifyResToParent;
    if (IsColdRunMode(appSpawnContent)) {
        appSpawnContent->content.runAppSpawn = AppSpawnColdRun;
    } else {
        appSpawnContent->content.runAppSpawn = AppSpawnRun;
        appSpawnContent->content.coldStartApp = AppSpawnColdStartApp;

        int ret = CreateAppSpawnServer(&appSpawnContent->server, socketName);
        APPSPAWN_CHECK(ret == 0, AppSpawnDestroyContent(&appSpawnContent->content);
            return NULL, "Failed to create server");
    }
    appSpawnContent->content.enablePerfork = IsEnablePrefork();
    return &appSpawnContent->content;
}

AppSpawnContent *StartSpawnService(const AppSpawnStartArg *startArg, uint32_t argvSize, int argc, char *const argv[])
{
    APPSPAWN_CHECK(startArg != NULL && argv != NULL, return NULL, "Invalid start arg");
    AppSpawnStartArg *arg = (AppSpawnStartArg *)startArg;
    APPSPAWN_LOGV("Start appspawn argvSize %{public}d mode %{public}d service %{public}s",
        argvSize, arg->mode, arg->serviceName);

    if (arg->initArg) {
        int ret = memset_s(argv[0], argvSize, 0, (size_t)argvSize);
        APPSPAWN_CHECK(ret == EOK, return NULL, "Failed to memset argv[0]");
        ret = strncpy_s(argv[0], argvSize, arg->serviceName, strlen(arg->serviceName));
        APPSPAWN_CHECK(ret == EOK, return NULL, "Failed to copy service name %{public}s", arg->serviceName);
    }

    // load module appspawn/common
    StartAppspawnTrace("AppSpawnLoadCommonModules");
    AppSpawnLoadAutoRunModules(MODULE_COMMON);
    FinishAppspawnTrace();
    AppSpawnModuleMgrInstall(ASAN_MODULE_PATH);

    APPSPAWN_CHECK(LE_GetDefaultLoop() != NULL, return NULL, "Invalid default loop");
    AppSpawnContent *content = AppSpawnCreateContent(arg->socketName, argv[0], argvSize, arg->mode);
    APPSPAWN_CHECK(content != NULL, return NULL, "Failed to create content for %{public}s", arg->socketName);

    StartAppspawnTrace("AppSpawnLoadAutoRunModules");
    AppSpawnLoadAutoRunModules(arg->moduleType);  // load corresponding plugin according to startup mode
    FinishAppspawnTrace();
    int ret = ServerStageHookExecute(STAGE_SERVER_PRELOAD, content);   // Preload, prase the sandbox
    APPSPAWN_CHECK(ret == 0, AppSpawnDestroyContent(content);
        return NULL, "Failed to prepare load %{public}s result: %{public}d", arg->serviceName, ret);
#ifndef APPSPAWN_TEST
    if (content->runChildProcessor == NULL) {
        APPSPAWN_LOGE("ChildLooper is not registered for %{public}s", arg->serviceName);
        APPSPAWN_KLOGE("ChildLooper is not registered for %{public}s", arg->serviceName);
        AppSpawnDestroyContent(content);
        return NULL;
    }
#endif
    AddAppSpawnHook(STAGE_CHILD_PRE_RUN, HOOK_PRIO_LOWEST, AppSpawnClearEnv);
    if (arg->mode == MODE_FOR_APP_SPAWN) {
        SetParameter("bootevent.appspawn.started", "true");
    }
    return content;
}

static AppSpawnMsgNode *ProcessSpawnBegetctlMsg(AppSpawnConnection *connection, AppSpawnMsgNode *message)
{
    uint32_t len = 0;
    const char *cmdMsg = (const char *)GetAppSpawnMsgExtInfo(message, MSG_EXT_NAME_BEGET_PID, &len);
    APPSPAWN_CHECK(cmdMsg != NULL, return NULL, "Failed to get extInfo");
    AppSpawnedProcess *appInfo = GetSpawnedProcess(atoi(cmdMsg));
    APPSPAWN_CHECK(appInfo != NULL, return NULL, "Failed to get app info");
    AppSpawnMsgNode *msgNode = RebuildAppSpawnMsgNode(message, appInfo);
    APPSPAWN_CHECK(msgNode != NULL, return NULL, "Failed to rebuild app message node");
    int ret = DecodeAppSpawnMsg(msgNode);
    if (ret != 0) {
        DeleteAppSpawnMsg(&msgNode);
        return NULL;
    }
    return msgNode;
}

static void ProcessBegetCmdMsg(AppSpawnConnection *connection, AppSpawnMsgNode *message)
{
    AppSpawnMsg *msg = &message->msgHeader;
    if (!IsDeveloperModeOpen()) {
        SendResponse(connection, msg, APPSPAWN_DEBUG_MODE_NOT_SUPPORT, 0);
        DeleteAppSpawnMsg(&message);
        return;
    }
    AppSpawnMsgNode *msgNode = ProcessSpawnBegetctlMsg(connection, message);
    if (msgNode == NULL) {
        SendResponse(connection, msg, APPSPAWN_DEBUG_MODE_NOT_SUPPORT, 0);
        DeleteAppSpawnMsg(&message);
        return;
    }
    ProcessSpawnReqMsg(connection, msgNode);
    DeleteAppSpawnMsg(&message);
    DeleteAppSpawnMsg(&msgNode);
}


static int ProcessSpawnRemountMsg(AppSpawnConnection *connection, AppSpawnMsgNode *message)
{
    APPSPAWN_LOGI("ProcessSpawnRemountMsg, do not handle it");
    return 0;
}

static void ProcessSpawnRestartMsg(AppSpawnConnection *connection, AppSpawnMsgNode *message)
{
    APPSPAWN_LOGI("ProcessSpawnRestartMsg, do not handle it");
    SendResponse(connection, &message->msgHeader, 0, 0);
}

/**
 * @brief 添加已生成的进程信息到进程管理器
 *
 * @param property 孵化上下文
 * @param connection 连接对象
 * @param message 消息节点
 * @return 0 on success, error code on failure
 */
static int AddSpawnedProcessInfo(AppSpawningCtx *property, AppSpawnConnection *connection,
    AppSpawnMsgNode *message)
{
    if (!CheckAppMsgFlagsSet(property, APP_FLAGS_SPAWN_IMAGE_PROCESS)) {
        const AppSpawnMsgAccessToken *tokenInfo = (const AppSpawnMsgAccessToken *)
            GetAppSpawnMsgInfo(property->message, TLV_ACCESS_TOKEN_INFO);
        uint64_t tokenid = (tokenInfo != NULL) ? tokenInfo->accessTokenIdEx : 0;
        AppSpawnedProcess *appInfo = AddSpawnedProcess(property->pid, GetBundleName(property),
            0, false, tokenid);
        APPSPAWN_CHECK(appInfo != NULL, return APPSPAWN_SYSTEM_ERROR,
            "Failed to add spawned process for worker pid=%{public}d", property->pid);

        AppSpawnMsgDacInfo *dacInfo = GetAppProperty(property, TLV_DAC_INFO);
        appInfo->uid = dacInfo != NULL ? dacInfo->uid : 0;
        appInfo->spmRefAdded = property->spmRefAdded;  // Copy SPM refcount flag
        WatchChildProcessFd(property);
        ProcessMgrHookExecute(STAGE_SERVER_APP_ADD, GetAppSpawnContent(), appInfo);
    }
    return 0;
}

/**
 * @brief 处理 checkpoint 进程创建请求(镜像进程和工作进程)
 *
 * 该函数独立处理 checkpoint 进程的创建,只执行 STAGE_PARENT_BOOT_IMG hook。
 * 与普通的进程创建(ProcessSpawnReqMsg)分离,避免耦合。
 *
 * @param connection 连接对象
 * @param message 消息节点
 */
APPSPAWN_STATIC void ProcessCheckpointReqMsg(AppSpawnConnection *connection, AppSpawnMsgNode *message)
{
    APPSPAWN_CHECK((connection != NULL && message != NULL), return, "Invalid input param");
    APPSPAWN_LOGI("ProcessCheckpointReqMsg: processName=%{public}s, msgType=%{public}u",
                  message->msgHeader.processName, message->msgHeader.msgType);

    int ret = CheckAppSpawnMsg(message);
    if (ret != 0) {
        APPSPAWN_LOGE("Checkpoint msg check failed: %{public}d", ret);
        SendResponseEx(connection, &message->msgHeader, ret, 0, 0);
        DeleteAppSpawnMsg(&message);
        return;
    }

    AppSpawningCtx *property = CreateAppSpawningCtx();
    if (property == NULL) {
        APPSPAWN_LOGE("Failed to create spawning context for checkpoint");
        SendResponseEx(connection, &message->msgHeader, APPSPAWN_SYSTEM_ERROR, 0, 0);
        DeleteAppSpawnMsg(&message);
        return;
    }

    property->state = APP_STATE_SPAWNING;
    property->message = message;
    message->connection = connection;
    ret = AppSpawnHookExecute(STAGE_PARENT_MSG_DECODE, HOOK_STOP_WHEN_ERROR, GetAppSpawnContent(), &property->client);
    APPSPAWN_ONLY_EXPER(ret != 0, APPSPAWN_LOGE("rebuild hook failed: %{public}d, aborting spawn", ret);
        SendResponse(connection, &message->msgHeader, ret, 0);
        DeleteAppSpawningCtx(property);
        return);
    ret = AppSpawnHookExecute(STAGE_PARENT_BOOT_IMG, HOOK_STOP_WHEN_ERROR, GetAppSpawnContent(), &property->client);
    if (ret != 0) {
        APPSPAWN_LOGE("STAGE_PARENT_BOOT_IMG hook failed: %{public}d", ret);
        ret = ret >= APPSPAWN_SYSTEM_ERROR ? FORK_ALL_INVALID : ret;
        SendResponseEx(connection, &message->msgHeader, ret, 0, 0);
        DeleteAppSpawningCtx(property);
        return;
    }
    ret = AddSpawnedProcessInfo(property, connection, message);
    APPSPAWN_CHECK(ret == 0,
        SendResponseEx(connection, &message->msgHeader, ret, 0, 0);
        DeleteAppSpawningCtx(property); return,
        "AddSpawnedProcessInfo failed %{public}d", ret);

    APPSPAWN_LOGI("Checkpoint process created successfully: pid=%{public}d, checkPointId=%{public}" PRId64"",
                  property->pid, property->checkPointId);

    SendResponseEx(connection, &message->msgHeader, ret, property->pid, property->checkPointId);
    DeleteAppSpawningCtx(property);
}

APPSPAWN_STATIC void ProcessUninstallDebugHap(AppSpawnConnection *connection, AppSpawnMsgNode *message)
{
    APPSPAWN_LOGI("ProcessUninstallDebugHap start");
    AppSpawningCtx *property = CreateAppSpawningCtx();
    if (property == NULL) {
        SendResponse(connection, &message->msgHeader, APPSPAWN_SYSTEM_ERROR, 0);
        DeleteAppSpawnMsg(&message);
        return;
    }

    property->message = message;
    property->message->connection = connection;
    int ret = AppSpawnHookExecute(STAGE_PARENT_UNINSTALL, 0, GetAppSpawnContent(), &property->client);
    SendResponse(connection, &message->msgHeader, ret, 0);
    DeleteAppSpawningCtx(property);
}

APPSPAWN_STATIC int AppspawpnDevicedebugKill(int pid, cJSON *args)
{
    cJSON *signal = cJSON_GetObjectItem(args, "signal");
    APPSPAWN_CHECK(cJSON_IsNumber(signal), return -1, "appspawn devicedebug json get signal fail");

    AppSpawnedProcess *appInfo = GetSpawnedProcess(pid);
    APPSPAWN_CHECK(appInfo != NULL, return APPSPAWN_DEVICEDEBUG_ERROR_APP_NOT_EXIST,
        "appspawn devicedebug get app info unsuccess, pid=%{public}d", pid);
    APPSPAWN_CHECK(appInfo->isDebuggable, return APPSPAWN_DEVICEDEBUG_ERROR_APP_NOT_DEBUGGABLE,
        "appspawn devicedebug process is not debuggable, pid=%{public}d", pid);

    APPSPAWN_LOGI("appspawn devicedebug debugable=%{public}d, pid=%{public}d, signal=%{public}d",
        appInfo->isDebuggable, pid, signal->valueint);

    if (kill(pid, signal->valueint) != 0) {
        APPSPAWN_LOGE("appspawn devicedebug unable to kill process, pid: %{public}d ret %{public}d", pid, errno);
        return -1;
    }

    return 0;
}

APPSPAWN_STATIC int AppspawnDevicedebugDeal(const char* op, int pid, cJSON *args)
{
    if (strcmp(op, "kill") == 0) {
        return AppspawpnDevicedebugKill(pid, args);
    }
    APPSPAWN_LOGE("appspawn devicedebug op:%{public}s invalid", op);

    return -1;
}

APPSPAWN_STATIC int ProcessAppSpawnDeviceDebugMsg(AppSpawnMsgNode *message)
{
    APPSPAWN_CHECK_ONLY_EXPER(message != NULL, return -1);
    uint32_t len = 0;

    const char* jsonString = (char *)GetAppSpawnMsgExtInfo(message, "devicedebug", &len);
    if (jsonString == NULL || len == 0) {
        APPSPAWN_LOGE("appspawn devicedebug get devicedebug fail");
        return -1;
    }

    cJSON *json = cJSON_Parse(jsonString);
    APPSPAWN_CHECK(json != NULL, return -1, "appspawn devicedebug json parse fail");

    cJSON *app = cJSON_GetObjectItem(json, "app");
    APPSPAWN_CHECK(cJSON_IsNumber(app), cJSON_Delete(json);
        return -1, "appspawn devicedebug json get app fail");

    cJSON *op = cJSON_GetObjectItem(json, "op");
    APPSPAWN_CHECK(cJSON_IsString(op) && op->valuestring != NULL, cJSON_Delete(json);
        return -1, "appspawn devicedebug json get op fail");

    cJSON *args = cJSON_GetObjectItem(json, "args");
    APPSPAWN_CHECK(cJSON_IsObject(args), cJSON_Delete(json);
        return -1, "appspawn devicedebug json get args fail");

    int result = AppspawnDevicedebugDeal(op->valuestring, app->valueint, args);
    cJSON_Delete(json);
    return result;
}

// ProcessAppSpawnLockStatusMsg: returns true if async (property owns message), false if sync
// On sync return, *result is set to the operation result for SendResponse
APPSPAWN_STATIC bool ProcessAppSpawnLockStatusMsg(AppSpawnConnection *connection,
    AppSpawnMsgNode *message, int *result)
{
    APPSPAWN_CHECK_ONLY_EXPER(message != NULL, return false);
    uint32_t len = 0;
    char *lockstatus = (char *)GetAppSpawnMsgExtInfo(message, "lockstatus", &len);
    APPSPAWN_CHECK(lockstatus != NULL, return false, "failed to get lockstatus");
    APPSPAWN_LOGI("appspawn get lockstatus %{public}s from storage_manager", lockstatus);
    char *userLockStatus = NULL;
    char *userIdStr = strtok_r(lockstatus, ":", &userLockStatus);
    APPSPAWN_CHECK(userIdStr != NULL && userLockStatus != NULL, return false,
        "lockstatus not satisfied format, failed to get userLockStatus");
    int userId = atoi(userIdStr);
    if (userId < USER_ID_MIN_VALUE || userId > USER_ID_MAX_VALUE) {
        APPSPAWN_LOGE("userId err %{public}s", userIdStr);
        return false;
    }
    if (strcmp(userLockStatus, "0") != 0 && strcmp(userLockStatus, "1") != 0) {
        APPSPAWN_LOGE("userLockStatus err %{public}s", userLockStatus);
        return false;
    }
    char lockStatusParam[LOCK_STATUS_PARAM_SIZE] = {0};
    int ret = snprintf_s(lockStatusParam, sizeof(lockStatusParam), sizeof(lockStatusParam) - 1,
        "startup.appspawn.lockstatus_%d", userId);
    APPSPAWN_CHECK(ret > 0, return false, "get lock status param failed, errno %{public}d", errno);
    ret = SetParameter(lockStatusParam, userLockStatus);
    APPSPAWN_CHECK(ret == 0, return false, "failed to set lockstatus param value ret %{public}d", ret);
#ifdef APPSPAWN_HISYSEVENT
    ReportKeyEvent(strcmp(userLockStatus, "0") == 0 ? UNLOCK_SUCCESS : LOCK_SUCCESS);
#endif
    if (strcmp(userLockStatus, "0") == 0) {
        bool async = HandleUnlockEvent(GetAppSpawnContent(), userId, message, connection);
        APPSPAWN_ONLY_EXPER(async,
            // Async path: property owns message, watcher callback will free it
            return true);

        // Sync path (L3): Both L1 and L2 failed, do serial mount in main process
        int mountResult = DoUnlockMountSerial(GetAppSpawnContent(), userId);
        *result = mountResult;
        APPSPAWN_LOGI("L3 serial mount done: uid=%{public}d result=%{public}d", userId, mountResult);
        return false;
    }
    return false;
}

APPSPAWN_STATIC int AppSpawnReqMsgFdGet(AppSpawnConnection *connection, AppSpawnMsgNode *message,
    const char *fdName, int *fd)
{
    APPSPAWN_CHECK_ONLY_EXPER(message != NULL && message->buffer != NULL && connection != NULL, return -1);
    APPSPAWN_CHECK_ONLY_EXPER(message->tlvOffset != NULL, return -1);
    int findFdIndex = 0;
    AppSpawnMsgReceiverCtx recvCtx = connection->receiverCtx;
    APPSPAWN_CHECK(recvCtx.fds != NULL && recvCtx.fdCount > 0, return 0,
        "no need get fd info %{public}d, %{public}d", recvCtx.fds != NULL, recvCtx.fdCount);

    for (uint32_t index = TLV_MAX; index < (TLV_MAX + message->tlvCount); index++) {
        if (message->tlvOffset[index] == INVALID_OFFSET) {
            return APPSPAWN_SYSTEM_ERROR;
        }
        uint8_t *data = message->buffer + message->tlvOffset[index];
        if (((AppSpawnTlv *)data)->tlvType != TLV_MAX) {
            continue;
        }
        AppSpawnTlvExt *tlv = (AppSpawnTlvExt *)data;
        if (strcmp(tlv->tlvName, MSG_EXT_NAME_APP_FD) != 0) {
            continue;
        }
        APPSPAWN_CHECK(findFdIndex < recvCtx.fdCount && recvCtx.fds[findFdIndex] > 0, return -1,
            "check get fd args failed %{public}d, %{public}d, %{public}d",
            findFdIndex, recvCtx.fdCount, recvCtx.fds[findFdIndex]);

        if (strcmp((const char *)(data + sizeof(AppSpawnTlvExt)), fdName) == 0 && recvCtx.fds[findFdIndex] > 0) {
            *fd = recvCtx.fds[findFdIndex];
            APPSPAWN_LOGI("Spawn Listen fd %{public}s get success %{public}d", fdName, recvCtx.fds[findFdIndex]);
            break;
        }
        findFdIndex++;
        if (findFdIndex >= recvCtx.fdCount) {
            break;
        }
    }
    return 0;
}

APPSPAWN_STATIC void ProcessObserveProcessSignalMsg(AppSpawnConnection *connection, AppSpawnMsgNode *message)
{
    APPSPAWN_CHECK_ONLY_EXPER(message != NULL, return);
    int fd = 0;
    int ret = AppSpawnReqMsgFdGet(connection, message, SPAWN_LISTEN_FD_NAME, &fd);
    if (fd <= 0 || ret != 0) {
        APPSPAWN_LOGE("Spawn Listen appspawn signal fd get unsuccess");
        SendResponse(connection, &message->msgHeader, APPSPAWN_SYSTEM_ERROR, 0);
        DeleteAppSpawnMsg(&message);
        return;
    }

    AppSpawnContent *content = GetAppSpawnContent();
    APPSPAWN_CHECK(content != NULL, return, "Spawn Listen appspawn content is null");
    // Prevent duplicate signalFd: close existing fd before assigning new one.
    // This ensures we don't leak the previous signalFd.
    APPSPAWN_ONLY_EXPER(content->signalFd > 0, close(content->signalFd));
    content->signalFd = fd;
    connection->receiverCtx.fdCount = 0;
    SendResponse(connection, &message->msgHeader, 0, 0);
    DeleteAppSpawnMsg(&message);
}

static void ProcessRecvMsg(AppSpawnConnection *connection, AppSpawnMsgNode *message)
{
    AppSpawnMsg *msg = &message->msgHeader;
    APPSPAWN_DUMPI("Recv msgHeader magic:0x%{public}x type:%{public}u id:%{public}u len:%{public}u %{public}s",
        msg->magic, msg->msgType, msg->msgId, msg->msgLen, msg->processName);
    APPSPAWN_CHECK_ONLY_LOG(connection->receiverCtx.nextMsgId == msg->msgId,
        "Invalid msg id %{public}u %{public}u", connection->receiverCtx.nextMsgId, msg->msgId);
    connection->receiverCtx.nextMsgId++;

    int ret;
    switch (msg->msgType) {
        case MSG_GET_RENDER_TERMINATION_STATUS: {  // get status
            AppSpawnResult result = {0};
            ret = ProcessTerminationStatusMsg(message, &result);
            SendResponse(connection, msg, ret == 0 ? result.result : ret, result.pid);
            DeleteAppSpawnMsg(&message);
            break;
        }
        case MSG_SPAWN_NATIVE_PROCESS:  // spawn msg
        case MSG_APP_SPAWN: {
            ProcessSpawnReqMsg(connection, message);
            break;
        }
        case MSG_DUMP:
            ProcessAppSpawnDumpMsg(message);
            SendResponse(connection, msg, 0, 0);
            DeleteAppSpawnMsg(&message);
            break;
        case MSG_BEGET_CMD: {
            ProcessBegetCmdMsg(connection, message);
            break;
        }
        case MSG_BEGET_SPAWNTIME:
            SendResponse(connection, msg, GetAppSpawnMgr()->spawnTime.minAppspawnTime,
                         GetAppSpawnMgr()->spawnTime.maxAppspawnTime);
            DeleteAppSpawnMsg(&message);
            break;
        case MSG_UPDATE_MOUNT_POINTS:
            ret = ProcessSpawnRemountMsg(connection, message);
            SendResponse(connection, msg, ret, 0);
            break;
        case MSG_RESTART_SPAWNER:
            ProcessSpawnRestartMsg(connection, message);
            break;
        case MSG_DEVICE_DEBUG:
            ret = ProcessAppSpawnDeviceDebugMsg(message);
            SendResponse(connection, msg, ret, 0);
            DeleteAppSpawnMsg(&message);
            break;
        case MSG_UNINSTALL_DEBUG_HAP:
            ProcessUninstallDebugHap(connection, message);
            break;
        case MSG_LOCK_STATUS: {
            APPSPAWN_LOGI("ProcessRecvMsg MSG_LOCK_STATUS: connId=%{public}u",
                connection ? connection->connectionId : 0);
            int result = 0;
            bool async = ProcessAppSpawnLockStatusMsg(connection, message, &result);
            APPSPAWN_LOGI("ProcessRecvMsg MSG_LOCK_STATUS: async=%{public}d result=%{public}d",
                async, result);
            // Async path: message owned by AppSpawningCtx, freed in watcher callback
            APPSPAWN_ONLY_EXPER(!async,
                SendResponse(connection, msg, result, 0);
                DeleteAppSpawnMsg(&message));
            break;
        }
        case MSG_OBSERVE_PROCESS_SIGNAL_STATUS:
            ProcessObserveProcessSignalMsg(connection, message);
            break;
        case MSG_UNLOAD_WEBLIB_IN_APPSPAWN:
            ret = ServerStageHookExecute(STAGE_SERVER_ARKWEB_UNLOAD, GetAppSpawnContent());
            SendResponse(connection, msg, ret, 0);
            DeleteAppSpawnMsg(&message);
            break;
        case MSG_LOAD_WEBLIB_IN_APPSPAWN:
            (void)ServerStageHookExecute(STAGE_SERVER_ARKWEB_PRELOAD, GetAppSpawnContent());
            SendResponse(connection, msg, 0, 0);
            DeleteAppSpawnMsg(&message);
            break;
        case MSG_SPAWN_IMAGE_PROCESS:
        case MSG_SPAWN_WORKER_PROCESS: {
            ProcessCheckpointReqMsg(connection, message);
            break;
        }
        default:
            SendResponse(connection, msg, APPSPAWN_MSG_INVALID, 0);
            DeleteAppSpawnMsg(&message);
            break;
    }
}

static void ReforkPreforkIfNeeded(AppSpawnContent *content)
{
    if (!content->enablePerfork) {
        return;
    }
    AppSpawningCtx *newProperty = CreateAppSpawningCtx();
    if (newProperty != NULL) {
        ProcessPreFork(content, newProperty);
        DeleteAppSpawningCtx(newProperty);
    }
}

static int TryLevel1PreforkUnlock(AppSpawnContent *content, int uid, AppSpawningCtx *property)
{
    APPSPAWN_ONLY_EXPER(content->reservedPid <= 0, return APPSPAWN_SYSTEM_ERROR);
    AppSpawnMgr *mgr = (AppSpawnMgr *)content;
    APPSPAWN_LOGI("TryLevel1PreforkUnlock: uid=%{public}d reservedPid=%{public}d",
        uid, content->reservedPid);

    do {
        AppSpawnFds *pcFds = FindSpawningFdsByPid(mgr, content->reservedPid, TYPE_PARENT_CHILD);
        APPSPAWN_CHECK(pcFds != NULL && pcFds->fds[1] >= 0 && SendUnlockMsgToPrefork(content, uid) == 0, break,
            "L1 failed: send unlock msg to prefork failed");

        AppSpawnFds *cpFds = FindSpawningFdsByPid(mgr, content->reservedPid, TYPE_CHILD_PARENT);
        APPSPAWN_CHECK(cpFds != NULL && cpFds->fds[0] >= 0, break,
            "L1 failed: childToParentFd not found");

        property->forkCtx.fd[0] = cpFds->fds[0];
        cpFds->fds[0] = -1;
        property->forkCtx.fd[1] = -1;
        property->pid = content->reservedPid;
        APPSPAWN_LOGI("L1 fd transfer done: fd[0]=%{public}d fd[1]=%{public}d pid=%{public}d",
            property->forkCtx.fd[0], property->forkCtx.fd[1], property->pid);

        APPSPAWN_CHECK(AddUnlockChildWatcher(property, UNLOCK_MOUNT_TIMEOUT_MS) == 0, break,
            "L1 AddUnlockChildWatcher failed");

#ifdef APPSPAWN_HISYSEVENT
        ReportKeyEvent(UNLOCK_MOUNT_L1_SUCCESS);
#endif
        UnregisterSpawningFdsByPid(mgr, content->reservedPid, TYPE_PARENT_CHILD);
        UnregisterSpawningFdsByPid(mgr, content->reservedPid, TYPE_CHILD_PARENT);
        content->reservedPid = 0;
        return 0;
    } while (0);

#ifdef APPSPAWN_HISYSEVENT
    ReportKeyEvent(UNLOCK_MOUNT_L1_FAIL);
#endif
    ClearPipeFd(property->forkCtx.fd, PIPE_FD_LENGTH);
    property->pid = 0;
    UnregisterSpawningFdsByPid(mgr, content->reservedPid, TYPE_PARENT_CHILD);
    UnregisterSpawningFdsByPid(mgr, content->reservedPid, TYPE_CHILD_PARENT);
    pid_t oldPid = content->reservedPid;
    content->reservedPid = 0;
    kill(oldPid, SIGKILL);
    return APPSPAWN_SYSTEM_ERROR;
}

APPSPAWN_STATIC bool HandleUnlockEvent(AppSpawnContent *content, int uid, AppSpawnMsgNode *message,
    AppSpawnConnection *connection)
{
    APPSPAWN_CHECK(content != NULL, return false, "HandleUnlockEvent: Invalid content");
    APPSPAWN_CHECK(uid > 0, return false, "HandleUnlockEvent: Invalid uid");
    APPSPAWN_CHECK(message != NULL, return false, "HandleUnlockEvent: Invalid message");
    APPSPAWN_CHECK(connection != NULL, return false, "HandleUnlockEvent: Invalid connection");

    APPSPAWN_LOGI("HandleUnlockEvent start: uid=%{public}d reservedPid=%{public}d",
        uid, content->reservedPid);

    // Create property context for L1/L2 async operations
    // NOTE: property->message and message->connection are used by L1/L2:
    // - L1/L2 watcher callbacks use them to SendResponse
    // - Watcher callbacks call DeleteAppSpawningCtx to free message
    // - DO NOT free message before watcher/timer callbacks complete
    AppSpawningCtx *property = CreateAppSpawningCtx();
    APPSPAWN_CHECK(property != NULL, return false, "Failed to create AppSpawningCtx");
    property->message = message;
    message->connection = connection;

    bool async = false;

    do {
        // Level 1: Reuse prefork child process (zero fork overhead)
        APPSPAWN_ONLY_EXPER(TryLevel1PreforkUnlock(content, uid, property) == 0,
            APPSPAWN_LOGI("HandleUnlockEvent L1 async started: pid=%{public}d", property->pid);
            async = true;
            break);

        // Level 2: Fork new child process with pipe for result
        APPSPAWN_ONLY_EXPER(ForkAndDoUnlockMount(content, uid, property) == 0, async = true;
            break);

        // Both L1 and L2 failed, caller will handle L3 serial mount
        APPSPAWN_LOGI("HandleUnlockEvent L1/L2 both failed, uid=%{public}d", uid);
        property->message = NULL;
        DeleteAppSpawningCtx(property);
    } while (0);

    ReforkPreforkIfNeeded(content);
    return async;
}

APPSPAWN_STATIC int SendUnlockMsgToPrefork(AppSpawnContent *content, int uid)
{
    APPSPAWN_CHECK(content != NULL, return APPSPAWN_ARG_INVALID,
                  "SendUnlockMsgToPrefork: Invalid content");

    // Get parentToChildFd from spawningFdsQueue
    AppSpawnMgr *mgr = (AppSpawnMgr *)content;
    AppSpawnFds *pcFds = FindSpawningFdsByPid(mgr, content->reservedPid, TYPE_PARENT_CHILD);
    APPSPAWN_CHECK(pcFds != NULL, return APPSPAWN_ARG_INVALID,
                  "parent child fds not found");

    AppSpawnPipeMsg msg = {0};
    msg.type = MSG_LOCK_STATUS;
    msg.msg.unlockMsg.uid = uid;

    ssize_t writeSize = write(pcFds->fds[1], &msg, sizeof(msg));
    if (writeSize != sizeof(msg)) {
        APPSPAWN_LOGE("SendUnlockMsgToPrefork write failed, ret=%{public}zd errno=%{public}d",
                      writeSize, errno);
        return APPSPAWN_PIPE_ERROR;
    }

    APPSPAWN_LOGI("SendUnlockMsgToPrefork success: uid=%{public}d fd=%{public}d",
        uid, pcFds->fds[1]);
    return 0;
}

APPSPAWN_STATIC int ForkAndDoUnlockMount(AppSpawnContent *content, int uid, AppSpawningCtx *property)
{
    APPSPAWN_CHECK(content != NULL, return APPSPAWN_ARG_INVALID, "ForkAndDoUnlockMount: Invalid content");
    APPSPAWN_CHECK(property != NULL, return APPSPAWN_ARG_INVALID, "ForkAndDoUnlockMount: Invalid property");
    APPSPAWN_CHECK(pipe(property->forkCtx.fd) == 0, return APPSPAWN_SYSTEM_ERROR,
        "ForkAndDoUnlockMount: pipe failed, errno=%{public}d", errno);
    APPSPAWN_LOGI("ForkAndDoUnlockMount: pipe created fd[0]=%{public}d fd[1]=%{public}d",
        property->forkCtx.fd[0], property->forkCtx.fd[1]);

    pid_t pid = fork();
    if (pid < 0) {
        APPSPAWN_LOGE("Fork for unlock failed, errno=%{public}d", errno);
        ClearPipeFd(property->forkCtx.fd, PIPE_FD_LENGTH);
        return APPSPAWN_SYSTEM_ERROR;
    }
    if (pid == 0) {
        HilogCloseSocketFd();
        int result = ProcessUnlockMessage(uid);
        SetUnlockMountResult(uid, result);
        APPSPAWN_LOGI("L2 child write result: uid=%{public}d result=%{public}d fd=%{public}d",
            uid, result, property->forkCtx.fd[1]);
        ssize_t writeSize = write(property->forkCtx.fd[1], &result, sizeof(result));
        APPSPAWN_CHECK_ONLY_LOG(writeSize == sizeof(result),
            "L2 write result failed %{public}zd %{public}d", writeSize, errno);
        close(property->forkCtx.fd[1]);
#ifdef APPSPAWN_HISYSEVENT
        ReportKeyEvent(result == 0 ? UNLOCK_SUCCESS : "UNLOCK_MOUNT_FAIL");
#endif
        ProcessExit(0);
    }
    // Parent: close write end, set pid
    close(property->forkCtx.fd[1]);
    property->forkCtx.fd[1] = -1;
    property->pid = pid;
    APPSPAWN_LOGI("ForkAndDoUnlockMount parent: pid=%{public}d fd[0]=%{public}d",
        pid, property->forkCtx.fd[0]);

    // Register watcher for async result
    APPSPAWN_CHECK(AddUnlockChildWatcher(property, UNLOCK_MOUNT_TIMEOUT_MS) == 0,
        kill(pid, SIGKILL);
        ClearPipeFd(property->forkCtx.fd, PIPE_FD_LENGTH);
        property->pid = 0;
        return APPSPAWN_SYSTEM_ERROR,
        "L2 AddUnlockChildWatcher failed");

    APPSPAWN_LOGI("L2 async started: pid=%{public}d fd[0]=%{public}d", pid, property->forkCtx.fd[0]);
#ifdef APPSPAWN_HISYSEVENT
    ReportKeyEvent(UNLOCK_MOUNT_L2_SUCCESS);
#endif
    return 0;
}

APPSPAWN_STATIC int DoUnlockMountSerial(AppSpawnContent *content, int uid)
{
    APPSPAWN_LOGI("DoUnlockMountSerial: uid=%{public}d", uid);

    int result = ProcessUnlockMessage(uid);

    SetUnlockMountResult(uid, result);

    APPSPAWN_LOGI("DoUnlockMountSerial done: uid=%{public}d result=%{public}d", uid, result);
#ifdef APPSPAWN_HISYSEVENT
    ReportKeyEvent(UNLOCK_MOUNT_L3_DONE);
#endif
    return result;
}

APPSPAWN_STATIC int ProcessUnlockMessage(int uid)
{
    APPSPAWN_LOGI("ProcessUnlockMessage start, uid=%{public}d", uid);

    // Set UID in content for hook function to retrieve
    AppSpawnContent *content = GetAppSpawnContent();
    if (content != NULL) {
        content->currentUnlockUid = uid;
    }

    // Save original scheduling policy and priority
    struct sched_param oldParam = { 0 };
    int oldSchedPolicy = sched_getscheduler(0);
    int ret = sched_getparam(0, &oldParam);
    APPSPAWN_CHECK_ONLY_LOG(ret == 0, "sched_getparam failed ret=%{public}d", ret);

    // Raise priority using SCHED_FIFO for mount operation
    struct sched_param param = { 0 };
    param.sched_priority = 1;
    ret = sched_setscheduler(0, SCHED_FIFO, &param);
    APPSPAWN_CHECK_ONLY_LOG(ret == 0, "sched_setscheduler failed ret=%{public}d", ret);

    // Execute shared mount via hook mechanism with trace
    StartAppspawnTrace("ProcessUnlockHook");
    int result = ServerStageHookExecute(STAGE_SERVER_LOCK, GetAppSpawnContent());
    FinishAppspawnTrace();

    // Restore original scheduling policy and priority
    ret = sched_setscheduler(0, oldSchedPolicy, &oldParam);
    APPSPAWN_CHECK_ONLY_LOG(ret == 0, "restore scheduler failed ret=%{public}d", ret);

    APPSPAWN_LOGI("ProcessUnlockMessage done, uid=%{public}d, result=%{public}d", uid, result);
    return result;
}