1190978c3Sopenharmony_ci/* 2190978c3Sopenharmony_ci * Copyright (c) 2021 Huawei Device Co., Ltd. 3190978c3Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 4190978c3Sopenharmony_ci * you may not use this file except in compliance with the License. 5190978c3Sopenharmony_ci * You may obtain a copy of the License at 6190978c3Sopenharmony_ci * 7190978c3Sopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 8190978c3Sopenharmony_ci * 9190978c3Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software 10190978c3Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 11190978c3Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12190978c3Sopenharmony_ci * See the License for the specific language governing permissions and 13190978c3Sopenharmony_ci * limitations under the License. 14190978c3Sopenharmony_ci */ 15190978c3Sopenharmony_ci 16190978c3Sopenharmony_ci#include "progress_thread.h" 17190978c3Sopenharmony_ci 18190978c3Sopenharmony_ci#include <unistd.h> 19190978c3Sopenharmony_ci#include <file_utils.h> 20190978c3Sopenharmony_ci 21190978c3Sopenharmony_ci#include "curl/curl.h" 22190978c3Sopenharmony_ci#include "curl/easy.h" 23190978c3Sopenharmony_ci 24190978c3Sopenharmony_ci#include "firmware_common.h" 25190978c3Sopenharmony_ci#include "update_define.h" 26190978c3Sopenharmony_ci#include "update_log.h" 27190978c3Sopenharmony_ci 28190978c3Sopenharmony_cinamespace OHOS { 29190978c3Sopenharmony_cinamespace UpdateEngine { 30190978c3Sopenharmony_cibool ProgressThread::isNoNet_ = false; 31190978c3Sopenharmony_cibool ProgressThread::isCancel_ = false; 32190978c3Sopenharmony_ci 33190978c3Sopenharmony_ciProgressThread::~ProgressThread() {} 34190978c3Sopenharmony_ci 35190978c3Sopenharmony_civoid ProgressThread::QuitDownloadThread() 36190978c3Sopenharmony_ci{ 37190978c3Sopenharmony_ci { 38190978c3Sopenharmony_ci std::unique_lock<std::mutex> lock(mutex_); 39190978c3Sopenharmony_ci isWake_ = true; 40190978c3Sopenharmony_ci isExitThread_ = true; 41190978c3Sopenharmony_ci condition_.notify_one(); 42190978c3Sopenharmony_ci } 43190978c3Sopenharmony_ci if (pDealThread_ != nullptr) { 44190978c3Sopenharmony_ci pDealThread_->join(); 45190978c3Sopenharmony_ci delete pDealThread_; 46190978c3Sopenharmony_ci pDealThread_ = nullptr; 47190978c3Sopenharmony_ci } 48190978c3Sopenharmony_ci} 49190978c3Sopenharmony_ci 50190978c3Sopenharmony_ciint32_t ProgressThread::StartProgress() 51190978c3Sopenharmony_ci{ 52190978c3Sopenharmony_ci std::unique_lock<std::mutex> lock(mutex_); 53190978c3Sopenharmony_ci if (pDealThread_ == nullptr) { 54190978c3Sopenharmony_ci pDealThread_ = new (std::nothrow)std::thread([this] { this->ExecuteThreadFunc(); }); 55190978c3Sopenharmony_ci ENGINE_CHECK(pDealThread_ != nullptr, return -1, "Failed to create thread"); 56190978c3Sopenharmony_ci } 57190978c3Sopenharmony_ci ENGINE_LOGI("StartProgress"); 58190978c3Sopenharmony_ci isWake_ = true; 59190978c3Sopenharmony_ci condition_.notify_one(); 60190978c3Sopenharmony_ci return 0; 61190978c3Sopenharmony_ci} 62190978c3Sopenharmony_ci 63190978c3Sopenharmony_civoid ProgressThread::StopProgress() 64190978c3Sopenharmony_ci{ 65190978c3Sopenharmony_ci std::unique_lock<std::mutex> lock(mutex_); 66190978c3Sopenharmony_ci isWake_ = true; 67190978c3Sopenharmony_ci isExitThread_ = false; 68190978c3Sopenharmony_ci condition_.notify_one(); 69190978c3Sopenharmony_ci} 70190978c3Sopenharmony_ci 71190978c3Sopenharmony_civoid ProgressThread::ExecuteThreadFunc() 72190978c3Sopenharmony_ci{ 73190978c3Sopenharmony_ci while (1) { 74190978c3Sopenharmony_ci { 75190978c3Sopenharmony_ci std::unique_lock<std::mutex> lock(mutex_); 76190978c3Sopenharmony_ci while (!isWake_) { 77190978c3Sopenharmony_ci ENGINE_LOGI("ExecuteThreadFunc wait"); 78190978c3Sopenharmony_ci condition_.wait(lock); 79190978c3Sopenharmony_ci } 80190978c3Sopenharmony_ci if (isExitThread_) { 81190978c3Sopenharmony_ci break; 82190978c3Sopenharmony_ci } 83190978c3Sopenharmony_ci isWake_ = false; 84190978c3Sopenharmony_ci } 85190978c3Sopenharmony_ci if (!ProcessThreadExecute()) { 86190978c3Sopenharmony_ci return; 87190978c3Sopenharmony_ci } 88190978c3Sopenharmony_ci } 89190978c3Sopenharmony_ci // thread exit and release resource 90190978c3Sopenharmony_ci ProcessThreadExit(); 91190978c3Sopenharmony_ci} 92190978c3Sopenharmony_ci 93190978c3Sopenharmony_ciint32_t DownloadThread::StartDownload(const std::string &fileName, const std::string &url) 94190978c3Sopenharmony_ci{ 95190978c3Sopenharmony_ci ENGINE_LOGI("StartDownload downloadFileName_ %s, serverUrl_ = %s", downloadFileName_.c_str(), serverUrl_.c_str()); 96190978c3Sopenharmony_ci downloadFileName_ = fileName; 97190978c3Sopenharmony_ci serverUrl_ = url; 98190978c3Sopenharmony_ci exitDownload_ = false; 99190978c3Sopenharmony_ci curl_global_init(CURL_GLOBAL_ALL); 100190978c3Sopenharmony_ci return StartProgress(); 101190978c3Sopenharmony_ci} 102190978c3Sopenharmony_ci 103190978c3Sopenharmony_civoid DownloadThread::StopDownload() 104190978c3Sopenharmony_ci{ 105190978c3Sopenharmony_ci ENGINE_LOGI("StopDownload"); 106190978c3Sopenharmony_ci exitDownload_ = true; 107190978c3Sopenharmony_ci StopProgress(); 108190978c3Sopenharmony_ci curl_global_cleanup(); 109190978c3Sopenharmony_ci} 110190978c3Sopenharmony_ci 111190978c3Sopenharmony_cibool DownloadThread::ProcessThreadExecute() 112190978c3Sopenharmony_ci{ 113190978c3Sopenharmony_ci ENGINE_LOGI("ProcessThreadExecute"); 114190978c3Sopenharmony_ci packageSize_ = GetLocalFileLength(downloadFileName_); 115190978c3Sopenharmony_ci ENGINE_LOGI("download packageSize_: %zu ", packageSize_); 116190978c3Sopenharmony_ci bool findDot = (downloadFileName_.find("/.") != std::string::npos) || 117190978c3Sopenharmony_ci (downloadFileName_.find("./") != std::string::npos); 118190978c3Sopenharmony_ci ENGINE_CHECK(!findDot, 119190978c3Sopenharmony_ci DownloadCallback(0, UpgradeStatus::DOWNLOAD_FAIL, "Failed to check file"); 120190978c3Sopenharmony_ci return true, "Failed to check file %s", downloadFileName_.c_str()); 121190978c3Sopenharmony_ci downloadFile_ = FileOpen(downloadFileName_, "ab+"); 122190978c3Sopenharmony_ci ENGINE_CHECK(downloadFile_ != nullptr, 123190978c3Sopenharmony_ci DownloadCallback(0, UpgradeStatus::DOWNLOAD_FAIL, "Failed ot open file"); 124190978c3Sopenharmony_ci return true, "Failed to open file %s", downloadFileName_.c_str()); 125190978c3Sopenharmony_ci 126190978c3Sopenharmony_ci downloadHandle_ = curl_easy_init(); 127190978c3Sopenharmony_ci ENGINE_CHECK(downloadHandle_ != nullptr, 128190978c3Sopenharmony_ci ProcessThreadExit(); 129190978c3Sopenharmony_ci DownloadCallback(0, UpgradeStatus::DOWNLOAD_FAIL, "Failed to init curl"); 130190978c3Sopenharmony_ci return true, "Failed to init curl"); 131190978c3Sopenharmony_ci 132190978c3Sopenharmony_ci curl_easy_setopt(downloadHandle_, CURLOPT_TIMEOUT, TIMEOUT_FOR_DOWNLOAD); 133190978c3Sopenharmony_ci curl_easy_setopt(downloadHandle_, CURLOPT_CONNECTTIMEOUT, TIMEOUT_FOR_CONNECT); 134190978c3Sopenharmony_ci curl_easy_setopt(downloadHandle_, CURLOPT_URL, serverUrl_.c_str()); 135190978c3Sopenharmony_ci curl_easy_setopt(downloadHandle_, CURLOPT_WRITEDATA, downloadFile_); 136190978c3Sopenharmony_ci curl_easy_setopt(downloadHandle_, CURLOPT_WRITEFUNCTION, WriteFunc); 137190978c3Sopenharmony_ci if (packageSize_ > 0) { 138190978c3Sopenharmony_ci curl_easy_setopt(downloadHandle_, CURLOPT_RESUME_FROM_LARGE, static_cast<curl_off_t>(packageSize_)); 139190978c3Sopenharmony_ci } 140190978c3Sopenharmony_ci curl_easy_setopt(downloadHandle_, CURLOPT_NOPROGRESS, 0L); 141190978c3Sopenharmony_ci curl_easy_setopt(downloadHandle_, CURLOPT_PROGRESSDATA, this); 142190978c3Sopenharmony_ci curl_easy_setopt(downloadHandle_, CURLOPT_PROGRESSFUNCTION, DownloadProgress); 143190978c3Sopenharmony_ci CURLcode res = curl_easy_perform(downloadHandle_); 144190978c3Sopenharmony_ci if (res != CURLE_OK) { 145190978c3Sopenharmony_ci ProcessThreadExit(); 146190978c3Sopenharmony_ci ENGINE_LOGI("Failed to download res %s", curl_easy_strerror(res)); 147190978c3Sopenharmony_ci if (res != CURLE_ABORTED_BY_CALLBACK) { // cancel by user, do not callback 148190978c3Sopenharmony_ci DownloadCallback(0, UpgradeStatus::DOWNLOAD_FAIL, 149190978c3Sopenharmony_ci std::to_string(CAST_INT(DownloadEndReason::CURL_ERROR))); 150190978c3Sopenharmony_ci } 151190978c3Sopenharmony_ci } else { 152190978c3Sopenharmony_ci ProcessThreadExit(); 153190978c3Sopenharmony_ci ENGINE_LOGI("Success to download"); 154190978c3Sopenharmony_ci DownloadCallback(DOWNLOAD_FINISH_PERCENT, UpgradeStatus::DOWNLOAD_SUCCESS, ""); 155190978c3Sopenharmony_ci } 156190978c3Sopenharmony_ci return false; 157190978c3Sopenharmony_ci} 158190978c3Sopenharmony_ci 159190978c3Sopenharmony_civoid DownloadThread::ProcessThreadExit() 160190978c3Sopenharmony_ci{ 161190978c3Sopenharmony_ci ENGINE_LOGI("ProcessThreadExit"); 162190978c3Sopenharmony_ci if (downloadHandle_ != nullptr) { 163190978c3Sopenharmony_ci curl_easy_cleanup(downloadHandle_); 164190978c3Sopenharmony_ci } 165190978c3Sopenharmony_ci downloadHandle_ = nullptr; 166190978c3Sopenharmony_ci if (downloadFile_ != nullptr) { 167190978c3Sopenharmony_ci fclose(downloadFile_); 168190978c3Sopenharmony_ci } 169190978c3Sopenharmony_ci downloadFile_ = nullptr; 170190978c3Sopenharmony_ci} 171190978c3Sopenharmony_ci 172190978c3Sopenharmony_ciint32_t DownloadThread::DownloadCallback(uint32_t percent, UpgradeStatus status, const std::string &error) 173190978c3Sopenharmony_ci{ 174190978c3Sopenharmony_ci if (exitDownload_) { 175190978c3Sopenharmony_ci ENGINE_LOGI("StopDownloadCallback"); 176190978c3Sopenharmony_ci return -1; 177190978c3Sopenharmony_ci } 178190978c3Sopenharmony_ci ENGINE_CHECK_NO_LOG(!DealAbnormal(percent), 179190978c3Sopenharmony_ci ENGINE_LOGI("DealAbnormal"); 180190978c3Sopenharmony_ci return -1); 181190978c3Sopenharmony_ci if (downloadProgress_.status == status && downloadProgress_.percent == percent) { 182190978c3Sopenharmony_ci // 避免回调过于频繁 183190978c3Sopenharmony_ci return 0; 184190978c3Sopenharmony_ci } 185190978c3Sopenharmony_ci ENGINE_LOGI("DownloadCallback percent %d, status %d, exitDownload_ %d, error %s, downloadFileName_ %s", 186190978c3Sopenharmony_ci percent, CAST_INT(status), exitDownload_ ? 1 : 0, error.c_str(), downloadFileName_.c_str()); 187190978c3Sopenharmony_ci if (status == UpgradeStatus::DOWNLOAD_FAIL) { 188190978c3Sopenharmony_ci if (access(downloadFileName_.c_str(), 0) == 0) { 189190978c3Sopenharmony_ci unlink(downloadFileName_.c_str()); 190190978c3Sopenharmony_ci } 191190978c3Sopenharmony_ci } else if (percent != DOWNLOAD_FINISH_PERCENT && 192190978c3Sopenharmony_ci (percent < (downloadProgress_.percent + DOWNLOAD_PERIOD_PERCENT))) { 193190978c3Sopenharmony_ci return 0; 194190978c3Sopenharmony_ci } 195190978c3Sopenharmony_ci 196190978c3Sopenharmony_ci // wait until the download is complete, and then make a notification 197190978c3Sopenharmony_ci if (percent == DOWNLOAD_FINISH_PERCENT 198190978c3Sopenharmony_ci && status == UpgradeStatus::DOWNLOADING) { 199190978c3Sopenharmony_ci return 0; 200190978c3Sopenharmony_ci } 201190978c3Sopenharmony_ci downloadProgress_.endReason = error; 202190978c3Sopenharmony_ci downloadProgress_.percent = percent; 203190978c3Sopenharmony_ci downloadProgress_.status = status; 204190978c3Sopenharmony_ci if (callback_ != nullptr) { 205190978c3Sopenharmony_ci callback_(serverUrl_, downloadFileName_, downloadProgress_); 206190978c3Sopenharmony_ci } 207190978c3Sopenharmony_ci return 0; 208190978c3Sopenharmony_ci} 209190978c3Sopenharmony_ci 210190978c3Sopenharmony_ciint32_t DownloadThread::DownloadProgress(const void *localData, 211190978c3Sopenharmony_ci double dlTotal, double dlNow, double ulTotal, double ulNow) 212190978c3Sopenharmony_ci{ 213190978c3Sopenharmony_ci ENGINE_CHECK_NO_LOG(dlTotal > 0, return 0); 214190978c3Sopenharmony_ci auto engine = reinterpret_cast<DownloadThread*>(const_cast<void*>(localData)); 215190978c3Sopenharmony_ci ENGINE_CHECK(engine != nullptr, return -1, "Can not find engine"); 216190978c3Sopenharmony_ci double curr = engine->GetPackageSize(); 217190978c3Sopenharmony_ci unsigned int percent = (dlNow + curr) / (curr + dlTotal) * DOWNLOAD_FINISH_PERCENT; 218190978c3Sopenharmony_ci return engine->DownloadCallback(percent, UpgradeStatus::DOWNLOADING, ""); 219190978c3Sopenharmony_ci} 220190978c3Sopenharmony_ci 221190978c3Sopenharmony_cisize_t DownloadThread::WriteFunc(void *ptr, size_t size, size_t nmemb, const void *stream) 222190978c3Sopenharmony_ci{ 223190978c3Sopenharmony_ci return fwrite(ptr, size, nmemb, reinterpret_cast<FILE*>(const_cast<void*>(stream))); 224190978c3Sopenharmony_ci} 225190978c3Sopenharmony_ci 226190978c3Sopenharmony_cisize_t DownloadThread::GetLocalFileLength(const std::string &fileName) 227190978c3Sopenharmony_ci{ 228190978c3Sopenharmony_ci bool findDot = (fileName.find("/.") != std::string::npos) || (fileName.find("./") != std::string::npos); 229190978c3Sopenharmony_ci ENGINE_CHECK_NO_LOG(!findDot, return 0); 230190978c3Sopenharmony_ci 231190978c3Sopenharmony_ci FILE* fp = FileOpen(fileName, "r"); 232190978c3Sopenharmony_ci ENGINE_CHECK_NO_LOG(fp != nullptr, return 0); 233190978c3Sopenharmony_ci int ret = fseek(fp, 0, SEEK_END); 234190978c3Sopenharmony_ci ENGINE_CHECK_NO_LOG(ret == 0, fclose(fp); 235190978c3Sopenharmony_ci return 0); 236190978c3Sopenharmony_ci size_t length = (size_t)ftell(fp); 237190978c3Sopenharmony_ci ret = fclose(fp); 238190978c3Sopenharmony_ci ENGINE_CHECK_NO_LOG(ret == 0, return 0); 239190978c3Sopenharmony_ci return length; 240190978c3Sopenharmony_ci} 241190978c3Sopenharmony_ci 242190978c3Sopenharmony_cibool DownloadThread::DealAbnormal(uint32_t percent) 243190978c3Sopenharmony_ci{ 244190978c3Sopenharmony_ci bool dealResult = false; 245190978c3Sopenharmony_ci if (isNoNet_ || isCancel_) { 246190978c3Sopenharmony_ci ENGINE_LOGI("No network or user cancel"); 247190978c3Sopenharmony_ci downloadProgress_.endReason = isNoNet_ ? std::to_string(CAST_INT(DownloadEndReason::NET_NOT_AVAILIABLE)) : 248190978c3Sopenharmony_ci std::to_string(CAST_INT(DownloadEndReason::CANCEL)); 249190978c3Sopenharmony_ci downloadProgress_.percent = percent; 250190978c3Sopenharmony_ci downloadProgress_.status = isNoNet_ ? UpgradeStatus::DOWNLOAD_FAIL : UpgradeStatus::DOWNLOAD_CANCEL; 251190978c3Sopenharmony_ci if (isCancel_) { 252190978c3Sopenharmony_ci isCancel_ = false; 253190978c3Sopenharmony_ci } 254190978c3Sopenharmony_ci dealResult = true; 255190978c3Sopenharmony_ci if (callback_ != nullptr) { 256190978c3Sopenharmony_ci callback_(serverUrl_, downloadFileName_, downloadProgress_); 257190978c3Sopenharmony_ci } 258190978c3Sopenharmony_ci } 259190978c3Sopenharmony_ci return dealResult; 260190978c3Sopenharmony_ci} 261190978c3Sopenharmony_ci 262190978c3Sopenharmony_ciFILE* DownloadThread::FileOpen(const std::string &fileName, const std::string &mode) 263190978c3Sopenharmony_ci{ 264190978c3Sopenharmony_ci if (fileName.empty() || fileName.size() > PATH_MAX) { 265190978c3Sopenharmony_ci ENGINE_LOGI("DownloadThread file is empty or exceed path_max"); 266190978c3Sopenharmony_ci return nullptr; 267190978c3Sopenharmony_ci } 268190978c3Sopenharmony_ci std::string fileDir = fileName; 269190978c3Sopenharmony_ci auto pos = fileDir.find_last_of("/"); 270190978c3Sopenharmony_ci if (pos != std::string::npos) { 271190978c3Sopenharmony_ci fileDir.erase(pos + 1); 272190978c3Sopenharmony_ci } else { 273190978c3Sopenharmony_ci ENGINE_LOGI("DownloadThread file %{public}s, mode: %{public}s", fileName.c_str(), mode.c_str()); 274190978c3Sopenharmony_ci return nullptr; 275190978c3Sopenharmony_ci } 276190978c3Sopenharmony_ci 277190978c3Sopenharmony_ci char *path = realpath(fileDir.c_str(), NULL); 278190978c3Sopenharmony_ci if (path == NULL) { 279190978c3Sopenharmony_ci ENGINE_LOGI("DownloadThread file %{public}s, mode: %{public}s", fileName.c_str(), mode.c_str()); 280190978c3Sopenharmony_ci return nullptr; 281190978c3Sopenharmony_ci } 282190978c3Sopenharmony_ci free(path); 283190978c3Sopenharmony_ci FILE* fp = fopen(fileName.c_str(), mode.c_str()); 284190978c3Sopenharmony_ci return fp; 285190978c3Sopenharmony_ci} 286190978c3Sopenharmony_ci} // namespace UpdateEngine 287190978c3Sopenharmony_ci} // namespace OHOS 288