• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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