• 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 "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