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