1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "progress_thread.h"
17
18 #include <unistd.h>
19 #include <file_utils.h>
20
21 #include "curl/curl.h"
22 #include "curl/easy.h"
23
24 #include "firmware_common.h"
25 #include "update_define.h"
26 #include "update_log.h"
27
28 namespace OHOS {
29 namespace UpdateEngine {
30 bool ProgressThread::isNoNet_ = false;
31 bool ProgressThread::isCancel_ = false;
32
~ProgressThread()33 ProgressThread::~ProgressThread() {}
34
QuitDownloadThread()35 void ProgressThread::QuitDownloadThread()
36 {
37 {
38 std::unique_lock<std::mutex> lock(mutex_);
39 isWake_ = true;
40 isExitThread_ = true;
41 condition_.notify_one();
42 }
43 if (pDealThread_ != nullptr) {
44 pDealThread_->join();
45 delete pDealThread_;
46 pDealThread_ = nullptr;
47 }
48 }
49
StartProgress()50 int32_t ProgressThread::StartProgress()
51 {
52 std::unique_lock<std::mutex> lock(mutex_);
53 if (pDealThread_ == nullptr) {
54 pDealThread_ = new (std::nothrow)std::thread([this] { this->ExecuteThreadFunc(); });
55 ENGINE_CHECK(pDealThread_ != nullptr, return -1, "Failed to create thread");
56 }
57 ENGINE_LOGI("StartProgress");
58 isWake_ = true;
59 condition_.notify_one();
60 return 0;
61 }
62
StopProgress()63 void ProgressThread::StopProgress()
64 {
65 std::unique_lock<std::mutex> lock(mutex_);
66 isWake_ = true;
67 isExitThread_ = false;
68 condition_.notify_one();
69 }
70
ExecuteThreadFunc()71 void ProgressThread::ExecuteThreadFunc()
72 {
73 while (1) {
74 {
75 std::unique_lock<std::mutex> lock(mutex_);
76 while (!isWake_) {
77 ENGINE_LOGI("ExecuteThreadFunc wait");
78 condition_.wait(lock);
79 }
80 if (isExitThread_) {
81 break;
82 }
83 isWake_ = false;
84 }
85 if (!ProcessThreadExecute()) {
86 return;
87 }
88 }
89 // thread exit and release resource
90 ProcessThreadExit();
91 }
92
StartDownload(const std::string & fileName,const std::string & url)93 int32_t DownloadThread::StartDownload(const std::string &fileName, const std::string &url)
94 {
95 ENGINE_LOGI("StartDownload downloadFileName_ %s, serverUrl_ = %s", downloadFileName_.c_str(), serverUrl_.c_str());
96 downloadFileName_ = fileName;
97 serverUrl_ = url;
98 exitDownload_ = false;
99 curl_global_init(CURL_GLOBAL_ALL);
100 return StartProgress();
101 }
102
StopDownload()103 void DownloadThread::StopDownload()
104 {
105 ENGINE_LOGI("StopDownload");
106 exitDownload_ = true;
107 StopProgress();
108 curl_global_cleanup();
109 }
110
ProcessThreadExecute()111 bool DownloadThread::ProcessThreadExecute()
112 {
113 ENGINE_LOGI("ProcessThreadExecute");
114 packageSize_ = GetLocalFileLength(downloadFileName_);
115 ENGINE_LOGI("download packageSize_: %zu ", packageSize_);
116 bool findDot = (downloadFileName_.find("/.") != std::string::npos) ||
117 (downloadFileName_.find("./") != std::string::npos);
118 ENGINE_CHECK(!findDot,
119 DownloadCallback(0, UpgradeStatus::DOWNLOAD_FAIL, "Failed to check file");
120 return true, "Failed to check file %s", downloadFileName_.c_str());
121 downloadFile_ = FileOpen(downloadFileName_, "ab+");
122 ENGINE_CHECK(downloadFile_ != nullptr,
123 DownloadCallback(0, UpgradeStatus::DOWNLOAD_FAIL, "Failed ot open file");
124 return true, "Failed to open file %s", downloadFileName_.c_str());
125
126 downloadHandle_ = curl_easy_init();
127 ENGINE_CHECK(downloadHandle_ != nullptr,
128 ProcessThreadExit();
129 DownloadCallback(0, UpgradeStatus::DOWNLOAD_FAIL, "Failed to init curl");
130 return true, "Failed to init curl");
131
132 curl_easy_setopt(downloadHandle_, CURLOPT_TIMEOUT, TIMEOUT_FOR_DOWNLOAD);
133 curl_easy_setopt(downloadHandle_, CURLOPT_CONNECTTIMEOUT, TIMEOUT_FOR_CONNECT);
134 curl_easy_setopt(downloadHandle_, CURLOPT_URL, serverUrl_.c_str());
135 curl_easy_setopt(downloadHandle_, CURLOPT_WRITEDATA, downloadFile_);
136 curl_easy_setopt(downloadHandle_, CURLOPT_WRITEFUNCTION, WriteFunc);
137 if (packageSize_ > 0) {
138 curl_easy_setopt(downloadHandle_, CURLOPT_RESUME_FROM_LARGE, static_cast<curl_off_t>(packageSize_));
139 }
140 curl_easy_setopt(downloadHandle_, CURLOPT_NOPROGRESS, 0L);
141 curl_easy_setopt(downloadHandle_, CURLOPT_PROGRESSDATA, this);
142 curl_easy_setopt(downloadHandle_, CURLOPT_PROGRESSFUNCTION, DownloadProgress);
143 CURLcode res = curl_easy_perform(downloadHandle_);
144 if (res != CURLE_OK) {
145 ProcessThreadExit();
146 ENGINE_LOGI("Failed to download res %s", curl_easy_strerror(res));
147 if (res != CURLE_ABORTED_BY_CALLBACK) { // cancel by user, do not callback
148 DownloadCallback(0, UpgradeStatus::DOWNLOAD_FAIL,
149 std::to_string(CAST_INT(DownloadEndReason::CURL_ERROR)));
150 }
151 } else {
152 ProcessThreadExit();
153 ENGINE_LOGI("Success to download");
154 DownloadCallback(DOWNLOAD_FINISH_PERCENT, UpgradeStatus::DOWNLOAD_SUCCESS, "");
155 }
156 return false;
157 }
158
ProcessThreadExit()159 void DownloadThread::ProcessThreadExit()
160 {
161 ENGINE_LOGI("ProcessThreadExit");
162 if (downloadHandle_ != nullptr) {
163 curl_easy_cleanup(downloadHandle_);
164 }
165 downloadHandle_ = nullptr;
166 if (downloadFile_ != nullptr) {
167 fclose(downloadFile_);
168 }
169 downloadFile_ = nullptr;
170 }
171
DownloadCallback(uint32_t percent,UpgradeStatus status,const std::string & error)172 int32_t DownloadThread::DownloadCallback(uint32_t percent, UpgradeStatus status, const std::string &error)
173 {
174 if (exitDownload_) {
175 ENGINE_LOGI("StopDownloadCallback");
176 return -1;
177 }
178 ENGINE_CHECK_NO_LOG(!DealAbnormal(percent),
179 ENGINE_LOGI("DealAbnormal");
180 return -1);
181 if (downloadProgress_.status == status && downloadProgress_.percent == percent) {
182 // 避免回调过于频繁
183 return 0;
184 }
185 ENGINE_LOGI("DownloadCallback percent %d, status %d, exitDownload_ %d, error %s, downloadFileName_ %s",
186 percent, CAST_INT(status), exitDownload_ ? 1 : 0, error.c_str(), downloadFileName_.c_str());
187 if (status == UpgradeStatus::DOWNLOAD_FAIL) {
188 if (access(downloadFileName_.c_str(), 0) == 0) {
189 unlink(downloadFileName_.c_str());
190 }
191 } else if (percent != DOWNLOAD_FINISH_PERCENT &&
192 (percent < (downloadProgress_.percent + DOWNLOAD_PERIOD_PERCENT))) {
193 return 0;
194 }
195
196 // wait until the download is complete, and then make a notification
197 if (percent == DOWNLOAD_FINISH_PERCENT
198 && status == UpgradeStatus::DOWNLOADING) {
199 return 0;
200 }
201 downloadProgress_.endReason = error;
202 downloadProgress_.percent = percent;
203 downloadProgress_.status = status;
204 if (callback_ != nullptr) {
205 callback_(serverUrl_, downloadFileName_, downloadProgress_);
206 }
207 return 0;
208 }
209
DownloadProgress(const void * localData,double dlTotal,double dlNow,double ulTotal,double ulNow)210 int32_t DownloadThread::DownloadProgress(const void *localData,
211 double dlTotal, double dlNow, double ulTotal, double ulNow)
212 {
213 ENGINE_CHECK_NO_LOG(dlTotal > 0, return 0);
214 auto engine = reinterpret_cast<DownloadThread*>(const_cast<void*>(localData));
215 ENGINE_CHECK(engine != nullptr, return -1, "Can not find engine");
216 double curr = engine->GetPackageSize();
217 unsigned int percent = (dlNow + curr) / (curr + dlTotal) * DOWNLOAD_FINISH_PERCENT;
218 return engine->DownloadCallback(percent, UpgradeStatus::DOWNLOADING, "");
219 }
220
WriteFunc(void * ptr,size_t size,size_t nmemb,const void * stream)221 size_t DownloadThread::WriteFunc(void *ptr, size_t size, size_t nmemb, const void *stream)
222 {
223 return fwrite(ptr, size, nmemb, reinterpret_cast<FILE*>(const_cast<void*>(stream)));
224 }
225
GetLocalFileLength(const std::string & fileName)226 size_t DownloadThread::GetLocalFileLength(const std::string &fileName)
227 {
228 bool findDot = (fileName.find("/.") != std::string::npos) || (fileName.find("./") != std::string::npos);
229 ENGINE_CHECK_NO_LOG(!findDot, return 0);
230
231 FILE* fp = FileOpen(fileName, "r");
232 ENGINE_CHECK_NO_LOG(fp != nullptr, return 0);
233 int ret = fseek(fp, 0, SEEK_END);
234 ENGINE_CHECK_NO_LOG(ret == 0, fclose(fp);
235 return 0);
236 size_t length = (size_t)ftell(fp);
237 ret = fclose(fp);
238 ENGINE_CHECK_NO_LOG(ret == 0, return 0);
239 return length;
240 }
241
DealAbnormal(uint32_t percent)242 bool DownloadThread::DealAbnormal(uint32_t percent)
243 {
244 bool dealResult = false;
245 if (isNoNet_ || isCancel_) {
246 ENGINE_LOGI("No network or user cancel");
247 downloadProgress_.endReason = isNoNet_ ? std::to_string(CAST_INT(DownloadEndReason::NET_NOT_AVAILIABLE)) :
248 std::to_string(CAST_INT(DownloadEndReason::CANCEL));
249 downloadProgress_.percent = percent;
250 downloadProgress_.status = isNoNet_ ? UpgradeStatus::DOWNLOAD_FAIL : UpgradeStatus::DOWNLOAD_CANCEL;
251 if (isCancel_) {
252 isCancel_ = false;
253 }
254 dealResult = true;
255 if (callback_ != nullptr) {
256 callback_(serverUrl_, downloadFileName_, downloadProgress_);
257 }
258 }
259 return dealResult;
260 }
261
FileOpen(const std::string & fileName,const std::string & mode)262 FILE* DownloadThread::FileOpen(const std::string &fileName, const std::string &mode)
263 {
264 if (fileName.empty() || fileName.size() > PATH_MAX) {
265 ENGINE_LOGI("DownloadThread file is empty or exceed path_max");
266 return nullptr;
267 }
268 std::string fileDir = fileName;
269 auto pos = fileDir.find_last_of("/");
270 if (pos != std::string::npos) {
271 fileDir.erase(pos + 1);
272 } else {
273 ENGINE_LOGI("DownloadThread file %{public}s, mode: %{public}s", fileName.c_str(), mode.c_str());
274 return nullptr;
275 }
276
277 char *path = realpath(fileDir.c_str(), NULL);
278 if (path == NULL) {
279 ENGINE_LOGI("DownloadThread file %{public}s, mode: %{public}s", fileName.c_str(), mode.c_str());
280 return nullptr;
281 }
282 free(path);
283 FILE* fp = fopen(fileName.c_str(), mode.c_str());
284 return fp;
285 }
286 } // namespace UpdateEngine
287 } // namespace OHOS
288