* Copyright (c) 2024-2026 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 "cj_request_task.h"
#include <cstring>
#include <fcntl.h>
#include <filesystem>
#include <fstream>
#include <regex>
#include <string>
#include <sys/stat.h>
#include <tuple>
#include <utility>
#include "application_context.h"
#include "cj_app_state_callback.h"
#include "cj_application_context.h"
#include "cj_initialize.h"
#include "cj_lambda.h"
#include "cj_request_common.h"
#include "cj_request_event.h"
#include "cj_response_listener.h"
#include "constant.h"
#include "request_common.h"
#include "log.h"
#include "request_manager.h"
#include "securec.h"
#include "storage_acl.h"
namespace OHOS::CJSystemapi::Request {
namespace fs = std::filesystem;
using OHOS::AbilityRuntime::Context;
using OHOS::Request::Action;
using OHOS::Request::ExceptionErrorCode;
using OHOS::Request::RequestManager;
using OHOS::Request::TaskInfo;
using OHOS::Request::Version;
using OHOS::StorageDaemon::AclSetAccess;
std::mutex CJRequestTask::taskMutex_;
std::map<std::string, std::shared_ptr<CJRequestTask>> CJRequestTask::taskMap_;
std::mutex CJRequestTask::pathMutex_;
std::map<std::string, int32_t> CJRequestTask::pathMap_;
std::map<std::string, std::tuple<bool, bool, uint32_t>> CJRequestTask::pathMapV2_;
bool CJRequestTask::register_ = false;
static constexpr int ACL_SUCC = 0;
static const std::string SA_PERMISSION_RWX = "g:3815:rwx";
static const std::string SA_PERMISSION_X = "g:3815:x";
static const std::string SA_PERMISSION_CLEAN = "g:3815:---";
static const std::string SA_PERMISSION_U_RW = "u:3815:rw";
static const std::string SA_PERMISSION_U_R = "u:3815:r";
static const std::string SA_PERMISSION_U_X = "u:3815:x";
static const std::string SA_PERMISSION_U_CLEAN = "u:3815:---";
CJRequestTask::CJRequestTask()
{
config_.version = Version::API10;
config_.action = Action::ANY;
skipPermissionCheck = true;
REQUEST_HILOGD("construct CJRequestTask()");
}
CJRequestTask::~CJRequestTask()
{
REQUEST_HILOGD("~CJRequestTask()");
RequestManager::GetInstance()->RemoveAllListeners(GetTidStr());
}
std::string CJRequestTask::GetTidStr() const
{
return tid_;
}
void CJRequestTask::SetTid()
{
tid_ = taskId_;
}
void CJRequestTask::AddTaskMap(const std::string &key, std::shared_ptr<CJRequestTask> task)
{
std::lock_guard<std::mutex> lockGuard(CJRequestTask::taskMutex_);
CJRequestTask::taskMap_[key] = std::move(task);
}
std::shared_ptr<CJRequestTask> CJRequestTask::FindTaskById(const std::string &taskId)
{
std::lock_guard<std::mutex> lockGuard(CJRequestTask::taskMutex_);
auto item = CJRequestTask::taskMap_.find(taskId);
if (item == CJRequestTask::taskMap_.end()) {
return nullptr;
}
return item->second;
}
void CJRequestTask::ClearTaskMap(const std::string &key)
{
std::shared_ptr<CJRequestTask> task;
{
std::lock_guard<std::mutex> lockGuard(CJRequestTask::taskMutex_);
auto it = taskMap_.find(key);
if (it == taskMap_.end()) {
return;
}
task = it->second;
taskMap_.erase(it);
}
}
bool CJRequestTask::SetPathPermission(const std::string &filepath, bool needWritePermission)
{
if (CheckApiVersionAfter19()) {
return SetPathPermissionSince20(filepath, needWritePermission);
}
std::string baseDir;
if (!CJInitialize::GetBaseDir(baseDir) || filepath.find(baseDir) == std::string::npos) {
REQUEST_HILOGE("File dir not found.");
return false;
}
AddPathMap(filepath, baseDir);
{
std::lock_guard<std::mutex> lockGuard(pathMutex_);
for (auto it : pathMap_) {
if (it.second <= 0) {
continue;
}
if (AclSetAccess(it.first, SA_PERMISSION_X) != ACL_SUCC) {
REQUEST_HILOGE("AclSetAccess Parent Dir Failed.");
}
}
}
if (AclSetAccess(filepath, SA_PERMISSION_RWX) != ACL_SUCC) {
REQUEST_HILOGE("AclSetAccess Child Dir Failed.");
return false;
}
return true;
}
static std::vector<std::string> SplitPath(const std::string &path)
{
std::vector<std::string> result;
if (path.empty() || path[0] != '/') {
return result;
}
result.reserve(std::count(path.begin(), path.end(), '/') + 1);
std::string currentPath;
size_t pos = 1;
while (pos < path.size()) {
size_t nextPos = path.find('/', pos);
if (nextPos == std::string::npos) {
nextPos = path.size();
}
if (nextPos > pos) {
currentPath += ("/" + path.substr(pos, nextPos - pos));
result.emplace_back(currentPath);
}
pos = nextPos + 1;
}
return result;
}
static std::vector<std::pair<std::string, bool>> SelectPath(const std::vector<std::string> &paths)
{
std::vector<std::pair<std::string, bool>> result;
if (paths.empty()) {
return result;
}
for (const auto &elem : paths) {
if ((elem.find(AREA1) == 0) || elem.find(AREA2) == 0 || elem.find(AREA5) == 0) {
result.emplace_back(elem, false);
}
}
if (!result.empty()) {
result.back().second = true;
}
return result;
}
static bool AddAcl(const std::string &path, bool isFile, bool needWritePermission)
{
std::string entry;
if (isFile) {
if (needWritePermission) {
entry = SA_PERMISSION_U_RW;
} else {
entry = SA_PERMISSION_U_R;
}
} else {
entry = SA_PERMISSION_U_X;
}
if (AclSetAccess(path, entry) != ACL_SUCC) {
REQUEST_HILOGE("AddAcl failed: %{public}s", path.c_str());
return false;
}
return true;
}
static bool SubAcl(const std::string &path)
{
if (AclSetAccess(path, SA_PERMISSION_U_CLEAN) != ACL_SUCC) {
REQUEST_HILOGE("SubAcl failed: %{public}s", path.c_str());
return false;
}
return true;
}
static void RollbackPaths(const std::vector<std::pair<std::string, bool>> &paths)
{
for (auto &path : paths) {
if (!CJRequestTask::SubOnePathToMap(path.first, path.second)) {
REQUEST_HILOGE("RollbackPaths SubOnePathToMap failed: %{public}s", path.first.c_str());
}
}
}
bool CJRequestTask::SetPathPermissionSince20(const std::string &filepath, bool needWritePermission)
{
std::vector<std::pair<std::string, bool>> paths = SelectPath(SplitPath(filepath));
if (paths.empty()) {
REQUEST_HILOGE("No valid path in AREA.");
return false;
}
std::vector<std::pair<std::string, bool>> completedPaths;
completedPaths.reserve(paths.size());
for (auto &elem : paths) {
if (!AddOnePathToMap(elem.first, elem.second, needWritePermission)) {
RollbackPaths(completedPaths);
return false;
}
completedPaths.emplace_back(elem);
}
return true;
}
bool CJRequestTask::SetDirsPermission(std::vector<std::string> &dirs)
{
if (dirs.empty()) {
return true;
}
std::string newPath = "/data/storage/el2/base/.ohos/.request/.certs";
std::vector<std::string> dirElems;
CJInitialize::StringSplit(newPath, '/', dirElems);
if (!CJInitialize::CreateDirs(dirElems)) {
REQUEST_HILOGE("CreateDirs Err: %{public}s", newPath.c_str());
return false;
}
for (const auto &folderPath : dirs) {
fs::path folder = folderPath;
if (!(fs::exists(folder) && fs::is_directory(folder))) {
return false;
}
for (const auto &entry : fs::directory_iterator(folder)) {
fs::path path = entry.path();
std::string existfilePath = folder.string() + "/" + path.filename().string();
std::string newfilePath = newPath + "/" + path.filename().string();
if (!fs::exists(newfilePath)) {
fs::copy(existfilePath, newfilePath);
}
if (chmod(newfilePath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) {
REQUEST_HILOGD("File add OTH access Failed.");
}
REQUEST_HILOGD("current filePath is %{public}s", newfilePath.c_str());
if (!CJRequestTask::SetPathPermission(newfilePath, false)) {
REQUEST_HILOGE("Set path permission fail.");
return false;
}
}
}
if (!dirs.empty()) {
dirs.clear();
dirs.push_back(newPath);
}
return true;
}
void CJRequestTask::AddPathMap(const std::string &filepath, const std::string &baseDir)
{
std::string childDir(filepath);
std::string parentDir;
while (childDir.length() > baseDir.length()) {
parentDir = childDir.substr(0, childDir.rfind("/"));
std::lock_guard<std::mutex> lockGuard(CJRequestTask::pathMutex_);
auto it = pathMap_.find(parentDir);
if (it == pathMap_.end()) {
pathMap_[parentDir] = 1;
} else {
pathMap_[parentDir] += 1;
}
childDir = parentDir;
}
}
void CJRequestTask::ResetDirAccess(const std::string &filepath)
{
int ret = AclSetAccess(filepath, SA_PERMISSION_CLEAN);
if (ret != ACL_SUCC) {
REQUEST_HILOGE("AclSetAccess Reset Dir Failed: %{public}s", filepath.c_str());
}
}
void CJRequestTask::RemovePathMap(const std::string &filepath)
{
if (CheckApiVersionAfter19()) {
RemovePathMapSince20(filepath);
return;
}
std::string baseDir;
if (!CJInitialize::GetBaseDir(baseDir) || filepath.find(baseDir) == std::string::npos) {
REQUEST_HILOGE("File dir not found.");
return;
}
if (chmod(filepath.c_str(), S_IRUSR | S_IWUSR | S_IRGRP) != 0) {
REQUEST_HILOGE("File remove WOTH access Failed.");
}
std::string childDir(filepath);
std::string parentDir;
while (childDir.length() > baseDir.length()) {
parentDir = childDir.substr(0, childDir.rfind("/"));
std::lock_guard<std::mutex> lockGuard(CJRequestTask::pathMutex_);
auto it = pathMap_.find(parentDir);
if (it != pathMap_.end()) {
if (pathMap_[parentDir] <= 1) {
pathMap_.erase(parentDir);
ResetDirAccess(parentDir);
} else {
pathMap_[parentDir] -= 1;
}
}
childDir = parentDir;
}
}
void CJRequestTask::RemovePathMapSince20(const std::string &filepath)
{
std::vector<std::pair<std::string, bool>> paths = SelectPath(SplitPath(filepath));
if (paths.empty()) {
return;
}
for (auto &elem : paths) {
if (!SubOnePathToMap(elem.first, elem.second)) {
REQUEST_HILOGE("RemovePathMap SubOnePathToMap failed: %{public}s", elem.first.c_str());
}
}
}
void CJRequestTask::RemoveDirsPermission(const std::vector<std::string> &dirs)
{
for (const auto &folderPath : dirs) {
fs::path folder = folderPath;
for (const auto &entry : fs::directory_iterator(folder)) {
fs::path path = entry.path();
std::string filePath = folder.string() + "/" + path.filename().string();
RemovePathMap(filePath);
}
}
}
void CJRequestTask::RegisterForegroundResume()
{
if (register_) {
return;
}
register_ = true;
auto context = ApplicationContextCJ::CJApplicationContext::GetInstance();
if (context == nullptr) {
REQUEST_HILOGE("Get ApplicationContext failed");
return;
}
context->RegisterAbilityLifecycleCallback(std::make_shared<CJAppStateCallback>());
REQUEST_HILOGD("Register foreground resume callback success");
}
ExceptionError CJRequestTask::Create(Context *context, Config &config)
{
int32_t seq = RequestManager::GetInstance()->GetNextSeq();
REQUEST_HILOGI("Begin task create, seq: %{public}d", seq);
config_ = config;
ExceptionError err;
RequestManager::GetInstance()->RestoreListener(CJRequestTask::ReloadListener);
if (config.mode == Mode::FOREGROUND) {
RegisterForegroundResume();
}
int32_t ret = RequestManager::GetInstance()->Create(config_, seq, taskId_);
if (ret != ExceptionErrorCode::E_OK) {
REQUEST_HILOGE("Create task failed, in");
err.code = static_cast<ExceptionErrorCode>(ret);
return err;
}
SetTid();
{
std::unique_lock<std::recursive_mutex> lock(listenerMutex_);
notifyDataListenerMap_[SubscribeType::REMOVE] =
std::make_shared<CJNotifyDataListener>(GetTidStr(), SubscribeType::REMOVE);
RequestManager::GetInstance()->AddListener(GetTidStr(), SubscribeType::REMOVE,
notifyDataListenerMap_[SubscribeType::REMOVE]);
}
AddTaskMap(GetTidStr(), shared_from_this());
return err;
}
ExceptionError CJRequestTask::GetTask(OHOS::AbilityRuntime::Context *context, std::string &taskId, std::string &token,
Config &config)
{
ExceptionError err;
int32_t seq = RequestManager::GetInstance()->GetNextSeq();
REQUEST_HILOGI("Begin get task, seq: %{public}d", seq);
auto task = CJRequestTask::FindTaskById(taskId);
if (task != nullptr) {
if (task->config_.token != token) {
return ConvertError(ExceptionErrorCode::E_TASK_NOT_FOUND);
}
config = task->config_;
return err;
}
int32_t result = RequestManager::GetInstance()->GetTask(taskId, token, config);
if (result != ExceptionErrorCode::E_OK) {
return ConvertError(result);
}
return err;
}
ExceptionError CJRequestTask::Remove(const std::string &tid)
{
int32_t result = RequestManager::GetInstance()->Remove(tid, Version::API10);
if (result != ExceptionErrorCode::E_OK) {
return ConvertError(result);
}
return ExceptionError();
}
ExceptionError CJRequestTask::Touch(const std::string &tid, TaskInfo &task, const std::string &token)
{
ExceptionError err;
int32_t result = RequestManager::GetInstance()->Touch(tid, token, task);
if (result != ExceptionErrorCode::E_OK) {
return ConvertError(result);
}
return err;
}
ExceptionError CJRequestTask::Show(const std::string &tid, TaskInfo &task)
{
ExceptionError err;
int32_t result = RequestManager::GetInstance()->Show(tid, task);
if (result != ExceptionErrorCode::E_OK) {
return ConvertError(result);
}
return err;
}
ExceptionError CJRequestTask::Search(const Filter &filter, std::vector<std::string> &tids)
{
ExceptionError err;
int32_t result = RequestManager::GetInstance()->Search(filter, tids);
if (result != ExceptionErrorCode::E_OK) {
return ConvertError(result);
}
return err;
}
void CJRequestTask::ReloadListener()
{
REQUEST_HILOGD("ReloadListener in");
std::lock_guard<std::mutex> lockGuard(CJRequestTask::taskMutex_);
RequestManager::GetInstance()->ReopenChannel();
for (const auto &it : taskMap_) {
RequestManager::GetInstance()->Subscribe(it.first);
}
}
ExceptionError CJRequestTask::On(std::string type, std::string &taskId, int64_t callback)
{
int32_t seq = RequestManager::GetInstance()->GetNextSeq();
REQUEST_HILOGI("Begin task on, seq: %{public}d", seq);
ExceptionError err;
SubscribeType subscribeType = CJRequestEvent::StringToSubscribeType(type);
if (subscribeType == SubscribeType::BUTT) {
err.code = ExceptionErrorCode::E_PARAMETER_CHECK;
err.errInfo = "First parameter error";
return err;
}
void *cb = reinterpret_cast<void *>(callback);
if (subscribeType == SubscribeType::RESPONSE) {
std::unique_lock<std::recursive_mutex> lock(listenerMutex_);
if (responseListener_ == nullptr) {
responseListener_ = std::make_shared<CJResponseListener>(GetTidStr());
}
responseListener_->AddListener(CJLambda::Create((void (*)(CResponse progress))cb), cb);
} else {
std::unique_lock<std::recursive_mutex> lock(listenerMutex_);
auto listener = notifyDataListenerMap_.find(subscribeType);
if (listener == notifyDataListenerMap_.end()) {
notifyDataListenerMap_[subscribeType] = std::make_shared<CJNotifyDataListener>(GetTidStr(), subscribeType);
}
notifyDataListenerMap_[subscribeType]->AddListener(CJLambda::Create((void (*)(CProgress progress))cb), cb);
}
REQUEST_HILOGI("End task on event %{public}s successfully, seq: %{public}d, tid: %{public}s", type.c_str(), seq,
GetTidStr().c_str());
return err;
}
ExceptionError CJRequestTask::Off(std::string event, int64_t callback)
{
int32_t seq = RequestManager::GetInstance()->GetNextSeq();
REQUEST_HILOGI("Begin task off, seq: %{public}d", seq);
ExceptionError err;
SubscribeType subscribeType = CJRequestEvent::StringToSubscribeType(event);
if (subscribeType == SubscribeType::BUTT) {
err.code = ExceptionErrorCode::E_PARAMETER_CHECK;
err.errInfo = "First parameter error";
return err;
}
CFunc cb = reinterpret_cast<CFunc>(callback);
if (subscribeType == SubscribeType::RESPONSE) {
std::unique_lock<std::recursive_mutex> lock(listenerMutex_);
if (responseListener_ == nullptr) {
responseListener_ = std::make_shared<CJResponseListener>(GetTidStr());
}
responseListener_->RemoveListener(cb);
} else {
std::unique_lock<std::recursive_mutex> lock(listenerMutex_);
if (notifyDataListenerMap_.find(subscribeType) == notifyDataListenerMap_.end()) {
notifyDataListenerMap_[subscribeType] = std::make_shared<CJNotifyDataListener>(GetTidStr(), subscribeType);
}
notifyDataListenerMap_[subscribeType]->RemoveListener(cb);
}
return err;
}
ExceptionError CJRequestTask::OnFailed(std::string &taskId, int64_t callback)
{
int32_t seq = RequestManager::GetInstance()->GetNextSeq();
REQUEST_HILOGI("Begin task on failed, seq: %{public}d", seq);
ExceptionError err;
std::shared_ptr<CJFailedListener> failedListener;
{
std::unique_lock<std::recursive_mutex> lock(listenerMutex_);
if (failedListener_ == nullptr) {
failedListener_ = std::make_shared<CJFailedListener>(GetTidStr());
}
failedListener = failedListener_;
}
void *cb = reinterpret_cast<void *>(callback);
failedListener->AddListener(CJLambda::Create((void (*)(int32_t))cb), cb);
REQUEST_HILOGI("End task on failed successfully, seq: %{public}d, tid: %{public}s", seq, GetTidStr().c_str());
return err;
}
ExceptionError CJRequestTask::OffFailed(int64_t callback)
{
int32_t seq = RequestManager::GetInstance()->GetNextSeq();
REQUEST_HILOGI("Begin task off failed, seq: %{public}d", seq);
ExceptionError err;
std::shared_ptr<CJFailedListener> failedListener;
{
std::unique_lock<std::recursive_mutex> lock(listenerMutex_);
if (failedListener_ == nullptr) {
failedListener_ = std::make_shared<CJFailedListener>(GetTidStr());
}
failedListener = failedListener_;
}
failedListener->RemoveListener(reinterpret_cast<CFunc>(callback));
return err;
}
void CJRequestTask::ClearTaskTemp(const std::string &tid, bool isRmFiles, bool isRmAcls, bool isRmCertsAcls)
{
std::lock_guard<std::mutex> lockGuard(CJRequestTask::taskMutex_);
auto item = CJRequestTask::taskMap_.find(tid);
if (item == CJRequestTask::taskMap_.end()) {
REQUEST_HILOGD("Clear task tmp files, not find task");
return;
}
auto task = item->second;
if (isRmFiles) {
auto bodyFileNames = task->config_.bodyFileNames;
for (auto &filePath : bodyFileNames) {
RemovePathMap(filePath);
RemoveFile(filePath);
}
}
if (isRmAcls) {
for (auto &file : task->config_.files) {
RemovePathMap(file.uri);
}
task->skipPermissionCheck = false;
}
if (isRmCertsAcls) {
RemoveDirsPermission(task->config_.certsPath);
}
}
bool CJRequestTask::AddOnePathToMap(const std::string &path, bool isFile, bool needWritePermission)
{
std::lock_guard<std::mutex> lockGuard(pathMutex_);
auto it = pathMapV2_.find(path);
if (it == pathMapV2_.end()) {
if (!AddAcl(path, isFile, needWritePermission)) {
return false;
}
pathMapV2_.emplace(path, std::tuple(isFile, needWritePermission, 1));
} else {
auto &[iFile, hasWritePermission, count] = it->second;
bool newHasWrite = hasWritePermission || needWritePermission;
if (!AddAcl(path, isFile, newHasWrite)) {
return false;
}
iFile = isFile;
hasWritePermission = newHasWrite;
count++;
}
return true;
}
bool CJRequestTask::SubOnePathToMap(const std::string &path, bool isFile)
{
std::lock_guard<std::mutex> lockGuard(pathMutex_);
auto it = pathMapV2_.find(path);
if (it == pathMapV2_.end()) {
REQUEST_HILOGE("SubOnePathToMap path not found: %{public}s", path.c_str());
return false;
}
auto &[iFile, hasWritePermission, count] = it->second;
if (iFile != isFile) {
REQUEST_HILOGE("SubOnePathToMap path changed: %{public}s", path.c_str());
}
if (count <= 0) {
REQUEST_HILOGE("SubOnePathToMap count is 0: %{public}s", path.c_str());
SubAcl(path);
pathMapV2_.erase(it);
return false;
}
count--;
if (count == 0) {
bool ret = SubAcl(path);
pathMapV2_.erase(it);
return ret;
}
return true;
}
}