* 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 "app_spawn_test_helper.h"
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <fcntl.h>
#include <pthread.h>
#include <csignal>
#include <string>
#include <sys/eventfd.h>
#include <sys/wait.h>
#include <unistd.h>
#include <inttypes.h>
#include "appspawn.h"
#include "appspawn_client.h"
#include "appspawn_modulemgr.h"
#include "appspawn_msg.h"
#include "appspawn_server.h"
#include "appspawn_service.h"
#include "appspawn_manager.h"
#include "appspawn_utils.h"
#include "loop_event.h"
#include "parameters.h"
#include "securec.h"
#include "app_spawn_stub.h"
namespace OHOS {
typedef struct {
int32_t bundleIndex;
char bundleName[APP_LEN_BUNDLE_NAME];
} AppBundleInfo;
typedef struct {
uint32_t hapFlags;
char apl[APP_APL_MAX_LEN];
} AppDomainInfo;
const uint32_t AppSpawnTestServer::defaultProtectTime = 60000;
uint32_t AppSpawnTestServer::serverId = 0;
static int TestChildLoopRun(AppSpawnContent *content, AppSpawnClient *client)
{
APPSPAWN_LOGV("ChildLoopRun ...");
AppSpawningCtx *property = (AppSpawningCtx *)client;
APPSPAWN_CHECK(content != NULL && property != NULL, return 0, "invalid param in clearEnv");
int fd = property->forkCtx.fd[1];
property->forkCtx.fd[1] = -1;
APPSPAWN_CHECK(fd >= 0, return 0, "invalid fd for notify parent");
int ret = 0;
(void)write(fd, &ret, sizeof(ret));
(void)close(fd);
sleep(1);
return 0;
}
AppSpawnTestServer::~AppSpawnTestServer()
{
if (localServer_) {
delete localServer_;
localServer_ = nullptr;
}
}
void AppSpawnTestServer::CloseCheckHandler(void)
{
APPSPAWN_LOGV("CloseCheckHandler");
#ifdef USER_TIMER_TO_CHECK
if (timer_ != nullptr) {
LE_StopTimer(LE_GetDefaultLoop(), timer_);
timer_ = nullptr;
}
#else
if (idle_) {
LE_DelIdle(idle_);
idle_ = nullptr;
}
#endif
}
void AppSpawnTestServer::StartCheckHandler(void)
{
#ifdef USER_TIMER_TO_CHECK
int ret = LE_CreateTimer(LE_GetDefaultLoop(), &timer_, ProcessIdle, this);
if (ret == 0) {
ret = LE_StartTimer(LE_GetDefaultLoop(), timer_, 100, 10000000);
}
#else
LE_AddIdle(LE_GetDefaultLoop(), &idle_, ProcessIdle, this, 10000000);
#endif
}
void AppSpawnTestServer::ServiceThread()
{
CmdArgs *args = nullptr;
pid_t pid = getpid();
APPSPAWN_LOGV("AppSpawnTestServer::ServiceThread %{public}s", serviceCmd_.c_str());
running_ = true;
if (testServer_) {
content_ = AppSpawnTestHelper::StartSpawnServer(serviceCmd_, args);
if (content_ == nullptr) {
return;
}
if (pid == getpid()) {
APPSPAWN_LOGV("Service start timer %{public}s ", serviceCmd_.c_str());
StartCheckHandler();
AppSpawnMgr *content = reinterpret_cast<AppSpawnMgr *>(content_);
APPSPAWN_CHECK_ONLY_EXPER(content != nullptr, return);
RegChildLooper(&content->content, TestChildLoopRun);
}
content_->runAppSpawn(content_, args->argc, args->argv);
if (pid != getpid()) {
exit(0);
} else {
content_ = nullptr;
}
} else {
StartCheckHandler();
localServer_ = new LocalTestServer();
localServer_->Run(APPSPAWN_SOCKET_NAME, recvMsgProcess_);
}
APPSPAWN_LOGV("AppSpawnTestServer::ServiceThread finish %{public}s ", serviceCmd_.c_str());
if (args) {
free(args);
}
return;
}
void AppSpawnTestServer::Start(void)
{
Start(nullptr);
}
static void *ServiceHelperThread(void *arg)
{
AppSpawnTestServer *server = reinterpret_cast<AppSpawnTestServer *>(arg);
APPSPAWN_LOGV("AppSpawnTestServer::thread ");
server->ServiceThread();
return nullptr;
}
void AppSpawnTestServer::Start(RecvMsgProcess process, uint32_t time)
{
APPSPAWN_LOGV("AppSpawnTestServer::Start serverId %{public}u", AppSpawnTestServer::serverId);
protectTime_ = time;
uint32_t retry = 0;
if (threadId_ != 0) {
return;
}
clock_gettime(CLOCK_MONOTONIC, &startTime_);
recvMsgProcess_ = process;
errno = 0;
int ret = 0;
do {
threadId_ = 0;
ret = pthread_create(&threadId_, nullptr, ServiceHelperThread, static_cast<void *>(this));
if (ret == 0) {
break;
}
APPSPAWN_LOGE("AppSpawnTestServer::Start create thread fail %{public}d %{public}d", ret, errno);
usleep(20000);
retry++;
} while (ret == EAGAIN && retry < 10);
retry = 0;
while (!running_ && (retry < 10)) {
usleep(20000);
retry++;
}
APPSPAWN_LOGV("AppSpawnTestServer::Start retry %{public}u", retry);
}
void AppSpawnTestServer::Stop()
{
APPSPAWN_LOGV("AppSpawnTestServer::Stop serverId %{public}u", AppSpawnTestServer::serverId);
if (threadId_ != 0) {
stop_ = true;
pthread_join(threadId_, nullptr);
threadId_ = 0;
APPSPAWN_LOGV("Stop");
}
}
void AppSpawnTestServer::KillNWebSpawnServer()
{
APPSPAWN_LOGV("Kill nwebspawn %{public}d", serverId_);
if (appPid_ > 0) {
kill(appPid_, SIGKILL);
}
}
void AppSpawnTestServer::StopSpawnService(void)
{
APPSPAWN_LOGV("StopSpawnService ");
if (serverStoped) {
CloseCheckHandler();
}
serverStoped = true;
if (testServer_) {
struct signalfd_siginfo siginfo = {};
siginfo.ssi_signo = SIGTERM;
siginfo.ssi_uid = 0;
ProcessSignal(&siginfo);
} else {
localServer_->Stop();
}
}
#ifdef USER_TIMER_TO_CHECK
void AppSpawnTestServer::ProcessIdle(const TimerHandle taskHandle, void *context)
#else
void AppSpawnTestServer::ProcessIdle(const IdleHandle taskHandle, void *context)
#endif
{
APPSPAWN_LOGV("AppSpawnTestServer::ProcessIdle");
AppSpawnTestServer *server = reinterpret_cast<AppSpawnTestServer *>(const_cast<void *>(context));
if (server->stop_) {
server->StopSpawnService();
return;
}
struct timespec end;
clock_gettime(CLOCK_MONOTONIC, &end);
uint64_t diff = DiffTime(&server->startTime_, &end);
if (diff >= (server->protectTime_ * 1000)) {
APPSPAWN_LOGV("AppSpawnTestServer:: timeout %{public}u %{public}" PRIu64 "", server->protectTime_, diff);
server->StopSpawnService();
return;
}
}
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);
errno = 0;
int recvLen = recvmsg(socketFd, &msg, flags);
APPSPAWN_CHECK_ONLY_LOG(errno == 0, "recvmsg with errno %d", errno);
struct cmsghdr *cmsg = nullptr;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr; 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 = reinterpret_cast<int*>(CMSG_DATA(cmsg));
APPSPAWN_CHECK(fdCount <= APP_MAX_FD_COUNT,
return -1, "failed to recv fd %d %d", connection->receiverCtx.fdCount, fdCount);
APPSPAWN_CHECK(memcpy_s(connection->receiverCtx.fds, fdCount * sizeof(int), fd,
fdCount * sizeof(int)) == 0, return -1, "memcpy_s fd failed");
connection->receiverCtx.fdCount = fdCount;
}
}
return recvLen;
}
int LocalTestServer::OnConnection(const LoopHandle loopHandle, const TaskHandle 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(TestConnection);
info.disConnectComplete = nullptr;
info.sendMessageComplete = SendMessageComplete;
info.recvMessage = OnReceiveRequest;
info.handleRecvMsg = HandleRecvMessage;
ServerInfo *serverInfo = (ServerInfo *)LE_GetUserData(server);
APPSPAWN_CHECK(serverInfo != nullptr, return -1, "Failed to alloc stream");
LE_STATUS ret = LE_AcceptStreamClient(loopHandle, server, &stream, &info);
APPSPAWN_CHECK(ret == 0, return -1, "Failed to alloc stream");
TestConnection *connection = (TestConnection *)LE_GetUserData(stream);
APPSPAWN_CHECK(connection != nullptr, return -1, "Failed to alloc stream");
connection->connectionId = ++connectionId;
connection->stream = stream;
connection->msgRecvLen = 0;
(void)memset_s(&connection->msg, sizeof(connection->msg), 0, sizeof(connection->msg));
connection->buffer = nullptr;
connection->recvMsgProcess = serverInfo->recvMsgProcess;
APPSPAWN_LOGI("OnConnection connection.id %{public}d fd %{public}d ",
connection->connectionId, LE_GetSocketFd(stream));
return 0;
}
void LocalTestServer::SendMessageComplete(const TaskHandle taskHandle, BufferHandle handle)
{
return;
}
void LocalTestServer::OnClose(const TaskHandle taskHandle)
{
TestConnection *connection = (TestConnection *)LE_GetUserData(taskHandle);
APPSPAWN_CHECK(connection != nullptr, return, "Invalid connection");
APPSPAWN_LOGI("OnClose connection.id %{public}d socket %{public}d",
connection->connectionId, LE_GetSocketFd(taskHandle));
AppSpawnConnection *spawnConnection = (AppSpawnConnection *) LE_GetUserData(taskHandle);
if (spawnConnection != nullptr) {
int fdCount = spawnConnection->receiverCtx.fdCount;
for (int i = 0; i < fdCount; i++) {
APPSPAWN_LOGI("OnClose close fd %d", spawnConnection->receiverCtx.fds[i]);
if (spawnConnection->receiverCtx.fds[i] >= 0) {
close(spawnConnection->receiverCtx.fds[i]);
}
}
}
}
void LocalTestServer::OnReceiveRequest(const TaskHandle taskHandle, const uint8_t *buffer, uint32_t buffLen)
{
TestConnection *connection = (TestConnection *)LE_GetUserData(taskHandle);
APPSPAWN_CHECK(connection != nullptr, LE_CloseTask(LE_GetDefaultLoop(), taskHandle);
return, "Failed to get client form socket");
if (connection->recvMsgProcess) {
connection->recvMsgProcess(connection, buffer, buffLen);
}
}
int LocalTestServer::Run(const char *socketName, RecvMsgProcess recvMsg)
{
char path[128] = {0};
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);
LE_StreamServerInfo info = {};
info.baseInfo.flags = TASK_STREAM | TASK_PIPE | TASK_SERVER;
info.baseInfo.userDataSize = sizeof(ServerInfo);
info.socketId = -1;
info.server = path;
info.baseInfo.close = nullptr;
info.incommingConnect = OnConnection;
MakeDirRec(path, 0711, 0);
ret = LE_CreateStreamServer(LE_GetDefaultLoop(), &serverHandle_, &info);
APPSPAWN_CHECK(ret == 0, return -1, "Failed to create socket for %{public}s errno: %{public}d", path, errno);
APPSPAWN_LOGI("LocalTestServer path %{public}s fd %{public}d", path, LE_GetSocketFd(serverHandle_));
ServerInfo *serverInfo = (ServerInfo *)LE_GetUserData(serverHandle_);
APPSPAWN_CHECK(serverInfo != nullptr, return -1, "Failed to alloc stream");
serverInfo->local = this;
serverInfo->recvMsgProcess = recvMsg;
LE_RunLoop(LE_GetDefaultLoop());
LE_CloseStreamTask(LE_GetDefaultLoop(), serverHandle_);
LE_StopLoop(LE_GetDefaultLoop());
LE_CloseLoop(LE_GetDefaultLoop());
APPSPAWN_LOGI("LocalTestServer exit");
return 0;
}
void LocalTestServer::Stop()
{
APPSPAWN_LOGI("Stop LocalTestServer ");
LE_StopLoop(LE_GetDefaultLoop());
}
int TestConnection::SendResponse(const AppSpawnMsg *msg, int result, pid_t pid)
{
APPSPAWN_LOGV("SendResponse result: %{public}d pid: %{public}d", result, pid);
uint32_t bufferSize = sizeof(AppSpawnResponseMsg);
BufferHandle handle = LE_CreateBuffer(LE_GetDefaultLoop(), bufferSize);
AppSpawnResponseMsg *buffer = (AppSpawnResponseMsg *)LE_GetBufferInfo(handle, nullptr, &bufferSize);
int ret = memcpy_s(buffer, bufferSize, msg, sizeof(AppSpawnMsg));
APPSPAWN_CHECK(ret == 0, return -1, "Failed to memcpy_s bufferSize");
buffer->result.result = result;
buffer->result.pid = pid;
return LE_Send(LE_GetDefaultLoop(), stream, handle, bufferSize);
}
uint32_t AppSpawnTestHelper::GenRandom(void)
{
uint32_t random = 0;
int fd = open("/dev/random", O_RDONLY);
if (fd >= 0) {
read(fd, &random, sizeof(random));
close(fd);
}
return random;
}
CmdArgs *AppSpawnTestHelper::ToCmdList(const char *cmd)
{
const uint32_t maxArgc = 20;
const uint32_t length = sizeof(CmdArgs) + maxArgc * sizeof(char *) + strlen(cmd) + APP_LEN_PROC_NAME + 1 + 2;
char *buffer = static_cast<char *>(malloc(length));
CmdArgs *args = reinterpret_cast<CmdArgs *>(buffer);
APPSPAWN_CHECK(buffer != nullptr, return nullptr, "Failed to alloc args");
(void)memset_s(args, length, 0, length);
char *start = buffer + sizeof(CmdArgs) + maxArgc * sizeof(char *);
char *end = buffer + length;
uint32_t index = 0;
char *curr = const_cast<char *>(cmd);
while (isspace(*curr)) {
curr++;
}
while (index < (maxArgc - 1) && *curr != '\0') {
if (args->argv[index] == nullptr) {
args->argv[index] = start;
}
*start = *curr;
if (isspace(*curr)) {
*start = '\0';
start = (index == 0) ? start + APP_LEN_PROC_NAME : start + 1;
while (isspace(*curr) && *curr != '\0') {
curr++;
}
if (*curr != '\0') {
index++;
}
} else {
start++;
curr++;
}
}
index++;
args->argv[index] = end - 2;
args->argv[index][0] = '#';
args->argv[index][1] = '\0';
args->argc = index + 1;
return args;
}
int AppSpawnTestHelper::AddDacInfo(AppSpawnReqMsgHandle &reqHandle)
{
AppDacInfo dacInfo = {};
dacInfo.uid = defaultTestUid_;
dacInfo.gid = defaultTestGid_;
dacInfo.gidCount = 2;
dacInfo.gidTable[0] = defaultTestGidGroup_;
dacInfo.gidTable[1] = defaultTestGidGroup_ + 1;
APPSPAWN_CHECK_ONLY_EXPER(strcpy_s(dacInfo.userName, sizeof(dacInfo.userName), "test-app-name") == 0,
return APPSPAWN_ARG_INVALID);
return AppSpawnReqMsgSetAppDacInfo(reqHandle, &dacInfo);
}
int AppSpawnTestHelper::AddFdInfo(AppSpawnReqMsgHandle &reqHandle)
{
if (fdArg < 0) {
fdArg = open("/dev/random", O_RDONLY);
}
APPSPAWN_LOGE("Add fd info %{public}d", fdArg);
APPSPAWN_CHECK(fdArg >= 0, return -1, "open fd failed ");
return AppSpawnReqMsgAddFd(reqHandle, "fdname", fdArg);
}
AppSpawnReqMsgHandle AppSpawnTestHelper::CreateMsg(AppSpawnClientHandle handle, uint32_t msgType, int base)
{
AppSpawnReqMsgHandle reqHandle = 0;
int ret = AppSpawnReqMsgCreate(static_cast<AppSpawnMsgType>(msgType), processName_.c_str(), &reqHandle);
APPSPAWN_CHECK(ret == 0, return INVALID_REQ_HANDLE, "Failed to create req %{public}s", processName_.c_str());
APPSPAWN_CHECK_ONLY_EXPER(msgType == MSG_APP_SPAWN || msgType == MSG_SPAWN_NATIVE_PROCESS, return reqHandle);
do {
ret = AddFdInfo(reqHandle);
APPSPAWN_CHECK(ret == 0, break, "Failed to add fd %{public}s", processName_.c_str());
ret = AppSpawnReqMsgSetBundleInfo(reqHandle, 100, processName_.c_str());
APPSPAWN_CHECK(ret == 0, break, "Failed to add bundle info req %{public}s", processName_.c_str());
ret = AddDacInfo(reqHandle);
APPSPAWN_CHECK(ret == 0, break, "Failed to add dac %{public}s", processName_.c_str());
ret = AppSpawnReqMsgSetAppAccessToken(reqHandle, accessTokenIdEx_);
APPSPAWN_CHECK(ret == 0, break, "Failed to add access token %{public}s", processName_.c_str());
if (defaultMsgFlags_ != 0) {
(void)AppSpawnReqMsgSetFlags(reqHandle, TLV_MSG_FLAGS, defaultMsgFlags_);
}
if (base) {
return reqHandle;
}
const char *testData = "ssssssssssssss sssssssss ssssssss";
ret = AppSpawnReqMsgAddExtInfo(reqHandle, "tlv-name-1",
reinterpret_cast<uint8_t *>(const_cast<char *>(testData)), strlen(testData));
APPSPAWN_CHECK(ret == 0, break, "Failed to ext tlv %{public}s", processName_.c_str());
size_t count = permissions_.size();
for (size_t i = 0; i < count; i++) {
ret = AppSpawnReqMsgAddPermission(reqHandle, permissions_[i]);
APPSPAWN_CHECK(ret == 0, break, "Failed to permission %{public}s", permissions_[i]);
}
ret = AppSpawnReqMsgSetAppInternetPermissionInfo(reqHandle, 1, 0);
APPSPAWN_CHECK(ret == 0, break, "Failed to internet info %{public}s", processName_.c_str());
ret = AppSpawnReqMsgSetAppOwnerId(reqHandle, ownerId_.c_str());
APPSPAWN_CHECK(ret == 0, break, "Failed to ownerid %{public}s", processName_.c_str());
const char *renderCmd = "/system/bin/sh ls -l #--ipc-fd=3#--shared-fd=4#--crash-fd=20";
ret = AppSpawnReqMsgAddExtInfo(reqHandle, MSG_EXT_NAME_RENDER_CMD,
reinterpret_cast<const uint8_t *>(renderCmd), strlen(renderCmd));
APPSPAWN_CHECK(ret == 0, break, "Failed to render cmd %{public}s", processName_.c_str());
ret = AppSpawnReqMsgSetAppDomainInfo(reqHandle, 1, defaultApl_.c_str());
APPSPAWN_CHECK(ret == 0, break, "Failed to domain info %{public}s", processName_.c_str());
return reqHandle;
} while (0);
AppSpawnReqMsgFree(reqHandle);
return INVALID_REQ_HANDLE;
}
AppSpawnReqMsgHandle AppSpawnTestHelper::CreateNWebMsg(AppSpawnClientHandle handle, uint32_t msgType, int base)
{
#define TEST_FILE_PERM 0644
if (renderIpcFd < 0) {
renderIpcFd = open("render_ipc.txt", O_RDONLY | O_CREAT, TEST_FILE_PERM);
APPSPAWN_LOGI("Add ipc fd info %{public}d", renderIpcFd);
}
if (renderSharedFd < 0) {
renderSharedFd = open("render_shared.txt", O_RDONLY | O_CREAT, TEST_FILE_PERM);
APPSPAWN_LOGI("Add shared fd info %{public}d", renderIpcFd);
}
if (renderCrashFd < 0) {
renderCrashFd = open("render_crash.txt", O_RDONLY | O_CREAT, TEST_FILE_PERM);
APPSPAWN_LOGI("Add crash fd info %{public}d", renderIpcFd);
}
if (renderIpcFd < 0 || renderSharedFd < 0 || renderCrashFd < 0) {
return INVALID_REQ_HANDLE;
}
auto reqHandle = CreateMsg(handle, msgType, base);
do {
int ret = AppSpawnReqMsgAddFd(reqHandle, "ipc-fd", renderIpcFd);
APPSPAWN_CHECK(ret == 0, break, "Failed to add fd %{public}s", processName_.c_str());
ret = AppSpawnReqMsgAddFd(reqHandle, "shared-fd", renderSharedFd);
APPSPAWN_CHECK(ret == 0, break, "Failed to add fd %{public}s", processName_.c_str());
ret = AppSpawnReqMsgAddFd(reqHandle, "crash-fd", renderCrashFd);
APPSPAWN_CHECK(ret == 0, break, "Failed to add fd %{public}s", processName_.c_str());
return reqHandle;
} while (0);
AppSpawnReqMsgFree(reqHandle);
return INVALID_REQ_HANDLE;
}
AppSpawnMsgNode *AppSpawnTestHelper::CreateAppSpawnMsg(AppSpawnMsg *msg)
{
AppSpawnMsgNode *msgNode = static_cast<AppSpawnMsgNode *>(calloc(1, sizeof(AppSpawnMsgNode)));
APPSPAWN_CHECK(msgNode != nullptr, return nullptr, "Failed to create receiver");
int ret = memcpy_s(&msgNode->msgHeader, sizeof(msgNode->msgHeader), msg, sizeof(msgNode->msgHeader));
APPSPAWN_CHECK(ret == 0, free(msgNode);
return nullptr, "Failed to memcpy msg");
msgNode->buffer = static_cast<uint8_t *>(malloc(msg->msgLen));
APPSPAWN_CHECK(msgNode->buffer != nullptr, free(msgNode);
return nullptr, "Failed to memcpy msg");
uint32_t totalCount = msg->tlvCount + TLV_MAX;
msgNode->tlvOffset = static_cast<uint32_t *>(malloc(totalCount * sizeof(uint32_t)));
APPSPAWN_CHECK(msgNode->tlvOffset != nullptr, free(msgNode);
return nullptr, "Failed to alloc memory for recv message");
for (uint32_t i = 0; i < totalCount; i++) {
msgNode->tlvOffset[i] = INVALID_OFFSET;
}
return msgNode;
}
AppSpawningCtx *AppSpawnTestHelper::GetAppProperty(AppSpawnClientHandle handle, AppSpawnReqMsgHandle reqHandle)
{
AppSpawnReqMsgNode *reqNode = static_cast<AppSpawnReqMsgNode *>(reqHandle);
APPSPAWN_CHECK(reqNode != nullptr && reqNode->msg != nullptr, AppSpawnReqMsgFree(reqHandle);
return nullptr, "Invalid reqNode");
AppSpawnMsgNode *msgNode = CreateAppSpawnMsg(reqNode->msg);
APPSPAWN_CHECK(msgNode != nullptr, return nullptr, "Failed to alloc for msg");
uint32_t bufferSize = reqNode->msg->msgLen;
uint32_t currIndex = 0;
uint32_t bufferStart = sizeof(AppSpawnMsg);
ListNode *node = reqNode->msgBlocks.next;
while (node != &reqNode->msgBlocks) {
AppSpawnMsgBlock *block = ListEntry(node, AppSpawnMsgBlock, node);
int ret = memcpy_s(msgNode->buffer + currIndex, bufferSize - currIndex,
block->buffer + bufferStart, block->currentIndex - bufferStart);
if (ret != 0) {
AppSpawnReqMsgFree(reqHandle);
DeleteAppSpawnMsg(&msgNode);
return nullptr;
}
currIndex += block->currentIndex - bufferStart;
bufferStart = 0;
node = node->next;
}
APPSPAWN_LOGV("GetAppProperty header magic 0x%{public}x type %{public}u id %{public}u len %{public}u %{public}s",
msgNode->msgHeader.magic, msgNode->msgHeader.msgType,
msgNode->msgHeader.msgId, msgNode->msgHeader.msgLen, msgNode->msgHeader.processName);
AppSpawnReqMsgFree(reqHandle);
int ret = DecodeAppSpawnMsg(msgNode);
APPSPAWN_CHECK(ret == 0, DeleteAppSpawnMsg(&msgNode);
return nullptr, "Decode msg fail");
AppSpawningCtx *property = CreateAppSpawningCtx();
APPSPAWN_CHECK_ONLY_EXPER(property != nullptr, DeleteAppSpawnMsg(&msgNode);
return nullptr);
property->message = msgNode;
SetDefaultTestData();
return property;
}
void AppSpawnTestHelper::SetDefaultTestData()
{
processName_ = std::string("com.example.myapplication");
defaultTestUid_ = 20010029;
defaultTestGid_ = 20010029;
defaultTestGidGroup_ = 20010029;
defaultTestBundleIndex_ = 100;
defaultApl_ = std::string("system_core");
defaultMsgFlags_ = 0;
accessTokenIdEx_ = 4832386455;
ownerId_ = "5765880207854616753";
}
int AppSpawnTestHelper::CreateSocket(int type)
{
const uint32_t maxCount = 10;
uint32_t count = 0;
int socketId = -1;
while ((socketId < 0) && (count < maxCount)) {
usleep(20000);
socketId = CreateClientSocket(type, 2);
if (socketId > 0) {
return socketId;
}
count++;
}
return socketId;
}
int AppSpawnTestHelper::CreateSendMsg(std::vector<uint8_t> &buffer, uint32_t msgType, uint32_t &msgLen,
const std::vector<AddTlvFunction> &addTlvFuncs)
{
if (buffer.size() < sizeof(AppSpawnMsg)) {
return -1;
}
AppSpawnMsg *msg = reinterpret_cast<AppSpawnMsg *>(buffer.data());
msg->magic = APPSPAWN_MSG_MAGIC;
msg->msgType = msgType;
msg->msgLen = sizeof(AppSpawnMsg);
msg->msgId = 1;
msg->tlvCount = 0;
APPSPAWN_CHECK_ONLY_EXPER(strcpy_s(msg->processName, sizeof(msg->processName), processName_.c_str()) == 0,
return -1);
uint32_t currLen = sizeof(AppSpawnMsg);
for (auto addTlvFunc : addTlvFuncs) {
uint32_t realLen = 0;
uint32_t tlvCount = 0;
int ret = addTlvFunc(buffer.data() + currLen, buffer.size() - currLen, realLen, tlvCount);
APPSPAWN_CHECK(ret == 0 && (currLen + realLen) < buffer.size(),
return -1, "Failed add tlv to msg %{public}s", processName_.c_str());
msg->msgLen += realLen;
currLen += realLen;
msg->tlvCount += tlvCount;
}
msgLen = msg->msgLen;
APPSPAWN_LOGV("CreateSendMsg msgLen %{public}d", msgLen);
return 0;
}
static int inline AddOneTlv(uint8_t *buffer, uint32_t bufferLen, const AppSpawnTlv &tlv, const uint8_t *data)
{
if (tlv.tlvLen > bufferLen) {
return -1;
}
int ret = memcpy_s(buffer, bufferLen, &tlv, sizeof(tlv));
APPSPAWN_CHECK(ret == 0, return -1, "Failed to memcpy_s bufferSize");
ret = memcpy_s(buffer + sizeof(tlv), bufferLen - sizeof(tlv), data, tlv.tlvLen - sizeof(tlv));
APPSPAWN_CHECK(ret == 0, return -1, "Failed to memcpy_s bufferSize");
return 0;
}
#define ADD_TLV(type, value, currLen, tlvCount) \
do { \
AppSpawnTlv d_tlv = {}; \
d_tlv.tlvType = (type); \
d_tlv.tlvLen = sizeof(AppSpawnTlv) + sizeof(value); \
ret = AddOneTlv(buffer + (currLen), bufferLen - (currLen), d_tlv, (uint8_t *)&(value)); \
APPSPAWN_CHECK(ret == 0, return -1, "Failed add tlv %{public}u", d_tlv.tlvType); \
(currLen) += d_tlv.tlvLen; \
(tlvCount)++; \
} while (0)
int AppSpawnTestHelper::AddBaseTlv(uint8_t *buffer, uint32_t bufferLen, uint32_t &realLen, uint32_t &tlvCount)
{
uint32_t currLen = 0;
uint32_t flags[3] = {2, 0b1010, 0};
AppSpawnTlv tlv = {};
tlv.tlvType = TLV_MSG_FLAGS;
tlv.tlvLen = sizeof(AppSpawnTlv) + sizeof(flags);
int ret = AddOneTlv(buffer + currLen, bufferLen - currLen, tlv, (uint8_t *)flags);
APPSPAWN_CHECK(ret == 0, return -1, "Failed add tlv %{public}u", tlv.tlvType);
currLen += tlv.tlvLen;
tlvCount++;
tlv.tlvType = TLV_PERMISSION;
tlv.tlvLen = sizeof(AppSpawnTlv) + sizeof(flags);
ret = AddOneTlv(buffer + currLen, bufferLen - currLen, tlv, (uint8_t *)flags);
APPSPAWN_CHECK(ret == 0, return -1, "Failed add tlv %{public}u", tlv.tlvType);
currLen += tlv.tlvLen;
tlvCount++;
AppDomainInfo domainInfo = {0, "normal"};
ADD_TLV(TLV_DOMAIN_INFO, domainInfo, currLen, tlvCount);
AppSpawnMsgAccessToken token = {12345678};
ADD_TLV(TLV_ACCESS_TOKEN_INFO, token, currLen, tlvCount);
AppSpawnMsgInternetInfo internetInfo = {0, 1};
ADD_TLV(TLV_INTERNET_INFO, internetInfo, currLen, tlvCount);
AppBundleInfo info = {};
APPSPAWN_CHECK_ONLY_EXPER(strcpy_s(info.bundleName, sizeof(info.bundleName), "test-bundleName") == 0,
return -1);
info.bundleIndex = 100;
ADD_TLV(TLV_BUNDLE_INFO, info, currLen, tlvCount);
AppDacInfo dacInfo = {};
dacInfo.uid = 20010029;
dacInfo.gid = 20010029;
dacInfo.gidCount = 2;
dacInfo.gidTable[0] = 20010029;
dacInfo.gidTable[1] = 20010030;
ADD_TLV(TLV_DAC_INFO, dacInfo, currLen, tlvCount);
realLen = currLen;
return 0;
}
AppSpawnContent *AppSpawnTestHelper::StartSpawnServer(std::string &cmd, CmdArgs *&args)
{
args = AppSpawnTestHelper::ToCmdList(cmd.c_str());
APPSPAWN_CHECK(args != nullptr, return nullptr, "Failed to alloc args");
static const std::unordered_map<std::string, AppSpawnStartArg> spawnConfigs = {
{"appspawn", {MODE_FOR_APP_SPAWN, MODULE_APPSPAWN, APPSPAWN_SOCKET_NAME, APPSPAWN_SERVER_NAME, 1}},
{"nwebspawn", {MODE_FOR_NWEB_SPAWN, MODULE_NWEBSPAWN, NWEBSPAWN_SOCKET_NAME, NWEBSPAWN_SERVER_NAME, 1}},
{"nativespawn", {MODE_FOR_NATIVE_SPAWN, MODULE_NATIVESPAWN, NATIVESPAWN_SOCKET_NAME,
NATIVESPAWN_SERVER_NAME, 1}},
{"cjappspawn", {MODE_FOR_CJAPP_SPAWN, MODULE_APPSPAWN, CJAPPSPAWN_SOCKET_NAME, CJAPPSPAWN_SERVER_NAME, 1}},
{"hybridspawn", {MODE_FOR_HYBRID_SPAWN, MODULE_HYBRIDSPAWN, HYBRIDSPAWN_SOCKET_NAME,
HYBRIDSPAWN_SERVER_NAME, 1}},
{"app_cold", {MODE_FOR_APP_COLD_RUN, MODULE_APPSPAWN, APPSPAWN_SOCKET_NAME, APPSPAWN_SERVER_NAME, 0}},
{"nweb_cold", {MODE_FOR_NWEB_COLD_RUN, MODULE_NWEBSPAWN, NWEBSPAWN_SOCKET_NAME, NWEBSPAWN_SERVER_NAME, 0}},
{"native_cold", {MODE_FOR_NATIVE_COLD_RUN, MODULE_NATIVESPAWN, NATIVESPAWN_SOCKET_NAME,
NATIVESPAWN_SERVER_NAME, 0}},
{"hybrid_cold", {MODE_FOR_HYBRID_COLD_RUN, MODULE_HYBRIDSPAWN, HYBRIDSPAWN_SOCKET_NAME,
HYBRIDSPAWN_SERVER_NAME, 0}},
{"cj_app_cold", {MODE_FOR_CJAPP_COLD_RUN, MODULE_APPSPAWN, CJAPPSPAWN_SOCKET_NAME, CJAPPSPAWN_SERVER_NAME, 0}}
};
AppSpawnStartArg startRrg = {};
std::string spawnType = (args->argc <= MODE_VALUE_INDEX) ? args->argv[0] : args->argv[MODE_VALUE_INDEX];
auto it = spawnConfigs.find(spawnType);
if (it != spawnConfigs.end()) {
const auto &config = it->second;
if (spawnType.find("_cold") != std::string::npos) {
APPSPAWN_CHECK(args->argc >= ARG_NULL, free(args);
return nullptr, "Invalid arg for cold start %{public}d", args->argc);
}
startRrg.mode = config.mode;
startRrg.moduleType = config.moduleType;
startRrg.socketName = config.socketName;
startRrg.serviceName = config.serviceName;
startRrg.initArg = config.initArg;
}
APPSPAWN_LOGV("Start service %{public}s", startRrg.serviceName);
AppSpawnContent *content = StartSpawnService(&startRrg, APP_LEN_PROC_NAME, args->argc, args->argv);
if (content == nullptr) {
free(args);
}
return content;
}
}
MODULE_CONSTRUCTOR(void)
{
MakeDirRec(APPSPAWN_MSG_DIR "appspawn", 0771, 1);
MakeDirRec(APPSPAWN_MSG_DIR "hybridspawn", 0771, 1);
MakeDirRec(APPSPAWN_MSG_DIR "nwebspawn", 0771, 1);
MakeDirRec(APPSPAWN_MSG_DIR "nativespawn", 0771, 1);
}