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