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 #include <unistd.h>
18 #include "curl/curl.h"
19 #include "curl/easy.h"
20 #include "update_helper.h"
21
22 namespace OHOS {
23 namespace UpdateEngine {
~ProgressThread()24 ProgressThread::~ProgressThread() {}
25
ExitThread()26 void ProgressThread::ExitThread()
27 {
28 {
29 std::unique_lock<std::mutex> lock(mutex_);
30 isWake_ = true;
31 isExitThread_ = true;
32 condition_.notify_one();
33 }
34 if (pDealThread_ != nullptr) {
35 pDealThread_->join();
36 delete pDealThread_;
37 pDealThread_ = nullptr;
38 }
39 }
40
StartProgress()41 int32_t ProgressThread::StartProgress()
42 {
43 std::unique_lock<std::mutex> lock(mutex_);
44 if (pDealThread_ == nullptr) {
45 pDealThread_ = new (std::nothrow)std::thread(&ProgressThread::ExecuteThreadFunc, this);
46 ENGINE_CHECK(pDealThread_ != nullptr, return -1, "Failed to create thread");
47 }
48 ENGINE_LOGI("StartProgress");
49 isWake_ = true;
50 condition_.notify_one();
51 return 0;
52 }
53
StopProgress()54 void ProgressThread::StopProgress()
55 {
56 std::unique_lock<std::mutex> lock(mutex_);
57 isWake_ = true;
58 isExitThread_ = false;
59 condition_.notify_one();
60 }
61
ExecuteThreadFunc()62 void ProgressThread::ExecuteThreadFunc()
63 {
64 while (1) {
65 {
66 std::unique_lock<std::mutex> lock(mutex_);
67 while (!isWake_) {
68 ENGINE_LOGI("ExecuteThreadFunc wait");
69 condition_.wait(lock);
70 }
71 if (isExitThread_) {
72 break;
73 }
74 isWake_ = false;
75 }
76 ProcessThreadExecute();
77 }
78 // thread exit and release resource
79 ProcessThreadExit();
80 }
81
StartDownload(const std::string & fileName,const std::string & url)82 int32_t DownloadThread::StartDownload(const std::string &fileName, const std::string &url)
83 {
84 downloadFileName_ = fileName;
85 serverUrl_ = url;
86 exitDownload_ = false;
87 curl_global_init(CURL_GLOBAL_ALL);
88 return StartProgress();
89 }
90
StopDownload()91 void DownloadThread::StopDownload()
92 {
93 ENGINE_LOGI("StopDownload ");
94 exitDownload_ = true;
95 StopProgress();
96 curl_global_cleanup();
97 }
98
ProcessThreadExecute()99 bool DownloadThread::ProcessThreadExecute()
100 {
101 packageSize_ = GetLocalFileLength(downloadFileName_);
102 ENGINE_LOGI("download packageSize_: %zu ", packageSize_);
103 bool findDot = (downloadFileName_.find("/.") != std::string::npos) ||
104 (downloadFileName_.find("./") != std::string::npos);
105 ENGINE_CHECK(!findDot,
106 DownloadCallback(0, UPDATE_STATE_DOWNLOAD_FAIL, "Failed to check file");
107 return true, "Failed to check file %s", downloadFileName_.c_str());
108 downloadFile_ = fopen(downloadFileName_.c_str(), "ab+");
109 ENGINE_CHECK(downloadFile_ != nullptr,
110 DownloadCallback(0, UPDATE_STATE_DOWNLOAD_FAIL, "Failed ot open file");
111 return true, "Failed to open file %s", downloadFileName_.c_str());
112
113 downloadHandle_ = curl_easy_init();
114 ENGINE_CHECK(downloadHandle_ != nullptr,
115 ProcessThreadExit();
116 DownloadCallback(0, UPDATE_STATE_DOWNLOAD_FAIL, "Failed to init curl");
117 return true, "Failed to init curl");
118
119 curl_easy_setopt(downloadHandle_, CURLOPT_TIMEOUT, TIMEOUT_FOR_DOWNLOAD);
120 curl_easy_setopt(downloadHandle_, CURLOPT_CONNECTTIMEOUT, TIMEOUT_FOR_CONNECT);
121 curl_easy_setopt(downloadHandle_, CURLOPT_URL, serverUrl_.c_str());
122 curl_easy_setopt(downloadHandle_, CURLOPT_WRITEDATA, downloadFile_);
123 curl_easy_setopt(downloadHandle_, CURLOPT_WRITEFUNCTION, WriteFunc);
124 if (packageSize_ > 0) {
125 curl_easy_setopt(downloadHandle_, CURLOPT_RESUME_FROM_LARGE, static_cast<curl_off_t>(packageSize_));
126 }
127 curl_easy_setopt(downloadHandle_, CURLOPT_NOPROGRESS, 0L);
128 curl_easy_setopt(downloadHandle_, CURLOPT_PROGRESSDATA, this);
129 curl_easy_setopt(downloadHandle_, CURLOPT_PROGRESSFUNCTION, DownloadProgress);
130 CURLcode res = curl_easy_perform(downloadHandle_);
131 if (res != CURLE_OK) {
132 ProcessThreadExit();
133 ENGINE_LOGI("Failed to download res %s", curl_easy_strerror(res));
134 if (res != CURLE_ABORTED_BY_CALLBACK) { // cancel by user, do not callback
135 DownloadCallback(0, UPDATE_STATE_DOWNLOAD_FAIL, curl_easy_strerror(res));
136 }
137 } else {
138 ProcessThreadExit();
139 ENGINE_LOGI("Success to download");
140 DownloadCallback(DOWNLOAD_FINISH_PERCENT, UPDATE_STATE_DOWNLOAD_SUCCESS, "");
141 }
142 // clear up
143 downloadProgress_.endReason = "";
144 downloadProgress_.percent = 0;
145 downloadProgress_.status = UPDATE_STATE_DOWNLOAD_ON;
146 return false;
147 }
148
ProcessThreadExit()149 void DownloadThread::ProcessThreadExit()
150 {
151 ENGINE_LOGI("ProcessThreadExit");
152 if (downloadHandle_ != nullptr) {
153 curl_easy_cleanup(downloadHandle_);
154 }
155 downloadHandle_ = nullptr;
156 if (downloadFile_ != nullptr) {
157 fclose(downloadFile_);
158 }
159 downloadFile_ = nullptr;
160 }
161
DownloadCallback(uint32_t percent,UpgradeStatus status,const std::string & error)162 int32_t DownloadThread::DownloadCallback(uint32_t percent, UpgradeStatus status, const std::string &error)
163 {
164 if (exitDownload_) {
165 ENGINE_LOGI("StopDownlDownloadCallbackoad");
166 return -1;
167 }
168 if (status == UPDATE_STATE_DOWNLOAD_FAIL) {
169 if (access(downloadFileName_.c_str(), 0) == 0) {
170 unlink(downloadFileName_.c_str());
171 }
172 } else if (percent != DOWNLOAD_FINISH_PERCENT
173 && (percent < (downloadProgress_.percent + DOWNLOAD_PERIOD_PERCENT))) {
174 return 0;
175 }
176
177 // wait until the download is complete, and then make a notification
178 if (percent == DOWNLOAD_FINISH_PERCENT
179 && status == UPDATE_STATE_DOWNLOAD_ON) {
180 return 0;
181 }
182 downloadProgress_.endReason = error;
183 downloadProgress_.percent = percent;
184 downloadProgress_.status = status;
185 if (callback_ != nullptr) {
186 callback_(downloadFileName_, downloadProgress_);
187 }
188 return 0;
189 }
190
DownloadProgress(const void * localData,double dlTotal,double dlNow,double ulTotal,double ulNow)191 int32_t DownloadThread::DownloadProgress(const void *localData,
192 double dlTotal, double dlNow, double ulTotal, double ulNow)
193 {
194 ENGINE_CHECK_NO_LOG(dlTotal > 0, return 0);
195 auto engine = reinterpret_cast<DownloadThread*>(const_cast<void*>(localData));
196 ENGINE_CHECK(engine != nullptr, return -1, "Can not find engine");
197 double curr = engine->GetPackageSize();
198 unsigned int percent = (dlNow + curr) / (curr + dlTotal) * DOWNLOAD_FINISH_PERCENT;
199 return engine->DownloadCallback(percent, UPDATE_STATE_DOWNLOAD_ON, "");
200 }
201
WriteFunc(void * ptr,size_t size,size_t nmemb,const void * stream)202 size_t DownloadThread::WriteFunc(void *ptr, size_t size, size_t nmemb, const void *stream)
203 {
204 return fwrite(ptr, size, nmemb, reinterpret_cast<FILE*>(const_cast<void*>(stream)));
205 }
206
GetLocalFileLength(const std::string & fileName)207 size_t DownloadThread::GetLocalFileLength(const std::string &fileName)
208 {
209 bool findDot = (fileName.find("/.") != std::string::npos) || (fileName.find("./") != std::string::npos);
210 ENGINE_CHECK_NO_LOG(!findDot, return 0);
211
212 FILE* fp = fopen(fileName.c_str(), "r");
213 ENGINE_CHECK_NO_LOG(fp != nullptr, return 0);
214 int ret = fseek(fp, 0, SEEK_END);
215 ENGINE_CHECK_NO_LOG(ret == 0, return 0);
216 size_t length = (size_t)ftell(fp);
217 ret = fclose(fp);
218 ENGINE_CHECK_NO_LOG(ret == 0, return 0);
219 return length;
220 }
221 } // namespace UpdateEngine
222 } // namespace OHOS
223