* Copyright (c) 2024-2025 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 "data_migration_manager.h"
#include <thread>
#include <fcntl.h>
#include <limits>
#include "font_define.h"
#include "font_hilog.h"
#include "font_manager_utils.h"
#include "directory_ex.h"
#include "hisysevent_adapter.h"
#include "font_config.h"
#include "storage_manager_adapter.h"
namespace OHOS {
namespace Global {
namespace FontManager {
namespace {
constexpr int32_t COPY_SPEED = 10 * 1024 / 60;
constexpr int32_t MAX_TRIGGER_COUNT = 100;
constexpr double EPSILON = 0.5;
static constexpr uint32_t HEARTBEAT_INTERVAL = 60;
static constexpr uint32_t BYTES_TO_MB_SHIFT = 20;
}
DataMigrationManager::DataMigrationManager()
{
}
DataMigrationManager::~DataMigrationManager()
{
}
void DataMigrationManager::DataMigration(const sptr<IDataMigrationCallback>& callback)
{
callback_ = callback;
isDataMigrationing_ = true;
int32_t ret = DataMigrationInner();
if (ret != ERR_OK) {
FONT_LOGE("FontManager DataMigration err.ErrCode:%{public}d", ret);
}
CheckAndUpdateAllFontRecord();
for (const auto& userId : userIds_) {
std::string installPath = INSTALL_PATH_PREFIX + std::to_string(userId) + INSTALL_PATH_SUFFIX;
StorageManagerAdapter::GetInstance()->ReportFontBundleStats(userId, installPath);
}
isDataMigrationing_ = false;
EventDataResult(ret);
ret = HisyseventAdapter::GetInstance()->CollectDataMigrationState(userIds_, ret);
if (ret != ERR_OK) {
FONT_LOGE("FontManager CollectDataMigrationState err.ErrCode:%{public}d", ret);
}
callback_ = nullptr;
}
int32_t DataMigrationManager::DataMigrationInner()
{
int32_t ret = InitDataMigrationEnv();
if (ret != ERR_OK) {
return ret;
}
StartHeartBeatTask();
return StartDataMigration();
}
int32_t DataMigrationManager::InitDataMigrationEnv()
{
userIds_ = FontManagerUtils::GetAllCreatedUserIds();
if (userIds_.empty()) {
FONT_LOGE("FontManager get all userIds err.");
return ERR_GET_ALL_USERIDS;
}
FontManagerUtils::DeleteDir(INSTALL_PATH_APP + TEMP_FILE, true);
if (OHOS::IsEmptyFolder(INSTALL_PATH_APP)) {
FONT_LOGE("FontManager INSTALL_PATH_APP is empty.");
return ERR_NOT_NEED_DATA_MIGRATION;
}
if (!CheckAllUserDir()) {
FONT_LOGE("FontManager CheckAllUserDir err.");
return ERR_CHECK_INSTALL_DIR;
}
if (!InitDataMigrationTempDir()) {
FONT_LOGE("FontManager InitDataMigrationTempDir err.");
return ERR_INIT_TEMP_DIR;
}
return ERR_OK;
}
int32_t DataMigrationManager::StartDataMigration()
{
std::vector<std::string> paths;
OHOS::GetDirFiles(INSTALL_PATH_APP, paths);
for (size_t i = 0; i < paths.size(); ++i) {
if (IsShouldUpdateProgress(i, paths.size())) {
EventDataProgress(i, paths.size(), userIds_.size());
}
int ret = StartOneFileCopyTask(paths[i]);
if (ret != ERR_OK) {
return ret;
}
FONT_LOGI("FontManager::FileCopyTask suc.FileName:%{public}s", FontManagerUtils::GetFileName(paths[i]).c_str());
}
return ERR_OK;
}
void DataMigrationManager::CheckAndUpdateAllFontRecord()
{
for (const auto& userId : userIds_) {
std::string installPath = INSTALL_PATH_PREFIX + std::to_string(userId) + INSTALL_PATH_SUFFIX;
auto config = FontConfig(installPath + FONT_CONFIG_FILE);
if (!config.CheckAndUpdateFontRecord()) {
FONT_LOGE("CheckAndUpdateFontRecord fail.userId: %{public}d", userId);
}
}
FONT_LOGI("CheckAndUpdateAllFontRecord suc.");
}
int32_t DataMigrationManager::StartOneFileCopyTask(const std::string& path)
{
for (const auto& userId : userIds_) {
int32_t ret = CopyFileForDataMigration(path, userId);
if (ret != ERR_OK) {
FONT_LOGE("StartOneFileCopyTask copy file %{public}s error", FontManagerUtils::GetFileName(path).c_str());
return ret;
}
}
if (!FontManagerUtils::RemoveFile(path)) {
FONT_LOGE("StartOneFileCopyTask RemoveFile file (%{public}s) err", FontManagerUtils::GetFileName(path).c_str());
return ERR_REMOVE_SRC_FILE;
}
return ERR_OK;
}
int32_t DataMigrationManager::CopyFileForDataMigration(const std::string &srcPath, const int32_t userId)
{
std::string fileName = FontManagerUtils::GetFileName(srcPath);
std::string tempPath = INSTALL_PATH_PREFIX + std::to_string(userId) + INSTALL_PATH_SUFFIX + TEMP_FILE + fileName;
std::string desPath = INSTALL_PATH_PREFIX + std::to_string(userId) + INSTALL_PATH_SUFFIX + fileName;
if (FontManagerUtils::CheckPathExist(desPath)) {
FONT_LOGI("CopyFileForDataMigration path is exist(%{public}s) ", fileName.c_str());
return ERR_OK;
}
FILE* fp = fopen(srcPath.c_str(), "rb");
if (!fp) {
FONT_LOGE("CopyFileForDataMigration open font file failed.");
return ERR_OPEN_SRC_FILE;
}
int fd = fileno(fp);
if (fd < 0) {
FONT_LOGE("CopyFileForDataMigration open font file failed, errno: %{public}d", errno);
(void)fclose(fp);
return ERR_OPEN_SRC_FILE;
}
if (!FontManagerUtils::CopyFile(fd, tempPath)) {
FONT_LOGE("CopyFileForDataMigration copy file %{public}s error", fileName.c_str());
(void)fclose(fp);
return ERR_COPY_FILE;
}
if (!FontManagerUtils::RenameFile(tempPath, desPath)) {
FONT_LOGE("CopyFileForDataMigration rename file %{public}s error", fileName.c_str());
FontManagerUtils::RemoveFile(tempPath);
(void)fclose(fp);
return ERR_RENAME_FILE;
}
(void)fclose(fp);
return ERR_OK;
}
void DataMigrationManager::StartHeartBeatTask()
{
std::weak_ptr<DataMigrationManager> weakPtr = shared_from_this();
std::thread([weakPtr]() {
FONT_LOGI("DataMigrationManager HeartBeat thread started.");
bool shouldRun = true;
while (shouldRun) {
{
auto self = weakPtr.lock();
if (!self || !self->isDataMigrationing_.load()) {
shouldRun = false;
continue;
}
FONT_LOGI("DataMigrationManager HeartBeat....");
self->EventDataHeartBeat();
}
std::this_thread::sleep_for(std::chrono::seconds(HEARTBEAT_INTERVAL));
}
FONT_LOGI("DataMigrationManager HeartBeat thread stopped.");
}).detach();
}
bool DataMigrationManager::IsShouldUpdateProgress(uint32_t i, uint32_t totalCount)
{
if (totalCount <= MAX_TRIGGER_COUNT) {
return true;
}
double step = static_cast<double>(totalCount) / static_cast<double>(MAX_TRIGGER_COUNT);
uint32_t expectedTriggerIndex = static_cast<uint32_t>(std::round(i / step));
double triggerPos = expectedTriggerIndex * step;
return std::fabs(i - triggerPos) < EPSILON;
}
void DataMigrationManager::EventDataProgress(uint32_t i, uint32_t size, uint32_t idsize)
{
std::uintmax_t folderSize = OHOS::GetFolderSize(INSTALL_PATH_APP);
std::uintmax_t remain = 0;
if (idsize > 0 && folderSize < (std::numeric_limits<std::uintmax_t>::max() / idsize)) {
remain = (folderSize * idsize) >> BYTES_TO_MB_SHIFT;
}
uint32_t timeRemaining = remain / COPY_SPEED;
if (timeRemaining < 1) {
timeRemaining = 1;
}
uint32_t progressPercentage =
i == 0 ? i : static_cast<uint32_t>((static_cast<uint64_t>(i) * MAX_TRIGGER_COUNT + size / 2) / size);
EventData eventData = {.event = EventType::PROGRESS_DOING,
.timeRemaining = timeRemaining,
.progressPercentage = progressPercentage};
RefreshEventData(eventData);
}
void DataMigrationManager::EventDataResult(int32_t result)
{
EventData eventData = {.event = EventType::PROGRESS_RESULT,
.progressResult = result};
RefreshEventData(eventData);
}
void DataMigrationManager::EventDataHeartBeat()
{
EventData eventData = {.event = EventType::HEART_BEAT};
RefreshEventData(eventData);
}
bool DataMigrationManager::CheckAllUserDir()
{
for (const auto& userId : userIds_) {
std::string path = INSTALL_PATH_PREFIX + std::to_string(userId) + INSTALL_PATH_SUFFIX;
if (!FontManagerUtils::CheckPathExist(path)) {
FONT_LOGE("CheckAllUserDir CheckUserDir err. userid = %{public}d.", userId);
return false;
}
}
return true;
}
bool DataMigrationManager::InitDataMigrationTempDir()
{
for (const auto& userId : userIds_) {
std::string tempPath = INSTALL_PATH_PREFIX + std::to_string(userId) + INSTALL_PATH_SUFFIX + TEMP_FILE;
if (!FontManagerUtils::CheckPathExist(tempPath)) {
if (!FontManagerUtils::CreateDirWithPermission(tempPath)) {
return false;
}
}
}
return true;
}
void DataMigrationManager::RefreshEventData(const EventData& eventData)
{
if (callback_ && callback_->AsObject() != nullptr) {
callback_->Handle(std::move(eventData));
}
}
}
}
}