• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 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 <file_utils.h>
19 #include <unistd.h>
20 #include <cinttypes>
21 
22 #include "curl/curl.h"
23 #include "curl/easy.h"
24 
25 #include "firmware_common.h"
26 #include "isys_installer.h"
27 #include "securec.h"
28 #include "sys_installer_kits_impl.h"
29 #include "update_define.h"
30 #include "update_log.h"
31 
32 namespace OHOS {
33 namespace UpdateService {
34 
StartDownload(const std::string & url,const int64_t size,const int64_t recordPoint)35 int32_t StreamProgressThread::StartDownload(const std::string &url, const int64_t size, const int64_t recordPoint)
36 {
37     ENGINE_LOGI("StartDownload url = %s", url.c_str());
38     serverUrl_ = url;
39     downloadedSize_ = recordPoint;
40     totalFileSize_ = size;
41     bufferPos_ = 0;
42     isCancel_ = false;
43     exitDownload_ = false;
44     (void)memset_s(buffer_, BUFFER_SIZE, 0, BUFFER_SIZE);
45     curl_global_init(CURL_GLOBAL_ALL);
46     return StartProgress();
47 }
48 
StopDownload()49 void StreamProgressThread::StopDownload()
50 {
51     ENGINE_LOGI("StopDownload");
52     exitDownload_ = true;
53     StopProgress();
54     curl_global_cleanup();
55 }
56 
ProcessThreadExecute()57 bool StreamProgressThread::ProcessThreadExecute()
58 {
59     ENGINE_LOGI("ProcessThreadExecute");
60     downloadHandle_ = curl_easy_init();
61     ENGINE_CHECK(downloadHandle_ != nullptr, ProcessThreadExit();
62                  DownloadCallback(0, UpgradeStatus::DOWNLOAD_FAIL, "Failed to init curl");
63                  return true, "Failed to init curl");
64     if (!CheckFileSize()) {
65         ProcessThreadExit();
66         DownloadCallback(0, UpgradeStatus::DOWNLOAD_FAIL, std::to_string(CAST_INT(DownloadEndReason::CURL_ERROR)));
67         return false;
68     }
69 
70     if (!DownloadFile()) {
71         ProcessThreadExit();
72         DownloadCallback(0, UpgradeStatus::DOWNLOAD_CANCEL,
73             std::to_string(CAST_INT(DownloadEndReason::CANCEL)));
74     } else {
75         ProcessThreadExit();
76         DownloadCallback(DOWNLOAD_FINISH_PERCENT, UpgradeStatus::DOWNLOAD_SUCCESS, "");
77     }
78 
79     return false;
80 }
81 
CheckFileSize()82 bool StreamProgressThread::CheckFileSize()
83 {
84     CURLcode res;
85     curl_easy_setopt(downloadHandle_, CURLOPT_TIMEOUT, TIMEOUT_FOR_DOWNLOAD);
86     curl_easy_setopt(downloadHandle_, CURLOPT_CONNECTTIMEOUT, TIMEOUT_FOR_CONNECT);
87     curl_easy_setopt(downloadHandle_, CURLOPT_URL, serverUrl_.c_str());
88     curl_easy_setopt(downloadHandle_, CURLOPT_NOBODY, 1L); // 只获取文件头部
89     curl_easy_setopt(downloadHandle_, CURLOPT_HEADER, 1L); // 包含响应头部
90     res = curl_easy_perform(downloadHandle_);
91     if (res != CURLE_OK) {
92         ENGINE_LOGE("Failed to curl_easy_perform res %s", curl_easy_strerror(res));
93         return false;
94     }
95 
96     // 获取文件大小
97     double fileSize = 0;
98     res = curl_easy_getinfo(downloadHandle_, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &fileSize);
99     if (res != CURLE_OK || fileSize <= 0 || fileSize != totalFileSize_) {
100         ENGINE_LOGE("Failed to curl_easy_getinfo res:%{public}s fileSize:%{public}f totalFileSize_:%{public}" PRId64 "",
101                     curl_easy_strerror(res), fileSize, totalFileSize_);
102         return false;
103     }
104     return true;
105 }
106 
DownloadFile()107 bool StreamProgressThread::DownloadFile()
108 {
109     CURLcode res;
110     curl_easy_setopt(downloadHandle_, CURLOPT_NOBODY, 0L); // 重置回去
111     curl_easy_setopt(downloadHandle_, CURLOPT_HEADER, 0L); // 重置回去
112     curl_easy_setopt(downloadHandle_, CURLOPT_WRITEDATA, this);
113     curl_easy_setopt(downloadHandle_, CURLOPT_WRITEFUNCTION, WriteFunc);
114 
115     ENGINE_LOGI("curl_easy_setopt downloadedSize_ %{public}" PRId64 "", downloadedSize_);
116     if (downloadedSize_ > 0) {
117         curl_easy_setopt(downloadHandle_, CURLOPT_RESUME_FROM_LARGE, static_cast<curl_off_t>(downloadedSize_));
118     }
119     curl_easy_setopt(downloadHandle_, CURLOPT_NOPROGRESS, 0L);
120     curl_easy_setopt(downloadHandle_, CURLOPT_PROGRESSDATA, this);
121     curl_easy_setopt(downloadHandle_, CURLOPT_PROGRESSFUNCTION, DownloadProgress);
122     res = curl_easy_perform(downloadHandle_);
123     if (res != CURLE_OK) {
124         ENGINE_LOGE("Failed to download res:%{public}s", curl_easy_strerror(res));
125         return false;
126     } else {
127         ENGINE_LOGI("Success to download");
128         return true;
129     }
130 }
131 
ProcessThreadExit()132 void StreamProgressThread::ProcessThreadExit()
133 {
134     ENGINE_LOGI("ProcessThreadExit");
135     if (downloadHandle_ != nullptr) {
136         curl_easy_cleanup(downloadHandle_);
137     }
138     downloadHandle_ = nullptr;
139 }
140 
DownloadCallback(uint32_t percent,UpgradeStatus status,const std::string & error)141 int32_t StreamProgressThread::DownloadCallback(uint32_t percent, UpgradeStatus status, const std::string &error)
142 {
143     ENGINE_LOGI("DownloadCallback percent:%{public}d, status:%{public}d, error:%{public}s",
144         percent, CAST_INT(status), error.c_str());
145 
146     downloadProgress_.endReason = error;
147     downloadProgress_.percent = percent;
148     downloadProgress_.status = status;
149     if (callback_ != nullptr) {
150         callback_(downloadProgress_);
151     }
152     return 0;
153 }
154 
DownloadProgress(const void * localData,double dlTotal,double dlNow,double ulTotal,double ulNow)155 int32_t StreamProgressThread::DownloadProgress(const void *localData, double dlTotal, double dlNow, double ulTotal,
156                                                double ulNow)
157 {
158     auto engine = reinterpret_cast<StreamProgressThread *>(const_cast<void *>(localData));
159     ENGINE_CHECK(engine != nullptr, return -1, "Can not find engine");
160     unsigned int percent = 0;
161     if (dlTotal > 0) {
162         percent = dlNow / dlTotal * DOWNLOAD_FINISH_PERCENT;
163         ENGINE_LOGI("StreamProgressThread DownloadProgress dlTotal:%{public}f,dlNow:%{public}f,ulTotal:%{public}f, "
164             "ulNow:%{public}f,percent:%{public}d", dlTotal, dlNow, ulTotal, ulNow, percent);
165     } else {
166         ENGINE_LOGI("StreamProgressThread DownloadProgress dlTotal:%{public}f is less than 0", dlTotal);
167         return 0;
168     }
169     return engine->DealExitOrCancel() ? -1 : 0;
170 }
171 
WriteFunc(uint8_t * ptr,size_t size,size_t nmemb,void * localData)172 size_t StreamProgressThread::WriteFunc(uint8_t *ptr, size_t size, size_t nmemb, void *localData)
173 {
174     ENGINE_LOGI("StreamProgressThread WriteFunc");
175     auto engine = reinterpret_cast<StreamProgressThread *>(const_cast<void *>(localData));
176     ENGINE_CHECK(engine != nullptr, return -1, "Can not find engine");
177     size_t totalSize = size * nmemb; // 本次收到的数据大小
178     uint8_t *buffer = engine->buffer_;
179     size_t& bufferPos = engine->bufferPos_;
180     int64_t& totalFileSize = engine->totalFileSize_;
181     int64_t& downloadedSize = engine->downloadedSize_;
182 
183     size_t processed = 0; // 已处理的数据量
184 
185     // 当还有未处理的数据时继续处理
186     while (processed < totalSize) {
187         size_t spaceLeft = BUFFER_SIZE - bufferPos - 1; // 当前缓冲区剩余空间
188         size_t bytesToCopy = std::min(spaceLeft, totalSize - processed);
189 
190         // 将数据复制到缓冲区
191         errno_t err = memcpy_s(buffer + bufferPos, BUFFER_SIZE - bufferPos, ptr + processed, bytesToCopy);
192         if (err != 0) {
193         ENGINE_LOGE("WriteFunc memcpy_s failed with error code: %{public}d", err);
194             return -1;
195         }
196         bufferPos += bytesToCopy;
197         processed += bytesToCopy;
198 
199         // 如果缓冲区已满,处理并清空
200         if (bufferPos == (BUFFER_SIZE - 1) || (downloadedSize + processed) == totalFileSize) {
201             ENGINE_LOGI("StreamProgressThread WriteFunc buffer full bufferPos:%{public}zu "
202                         "(downloadedSize+processed):%{public}" PRId64 "",
203                         bufferPos, downloadedSize + processed);
204             #ifndef UPDATER_UT
205             int32_t ret = SysInstaller::SysInstallerKitsImpl::GetInstance().ProcessStreamData(buffer, bufferPos);
206             if (ret != 0) {
207                 ENGINE_LOGE("WriteFunc ProcessStreamData failed");
208                 return -1;
209             }
210             #endif
211             // 清空缓冲区
212             (void)memset_s(buffer, BUFFER_SIZE, 0, BUFFER_SIZE);
213             bufferPos = 0;
214         }
215     }
216     downloadedSize += totalSize;
217 
218     return totalSize;
219 }
220 
DealExitOrCancel()221 bool StreamProgressThread::DealExitOrCancel()
222 {
223     if (exitDownload_) {
224         ENGINE_LOGI("DealExitOrCancel exit Download");
225         return true;
226     }
227     if (isCancel_) {
228         ENGINE_LOGI("DealExitOrCancel install task cancel");
229         return true;
230     }
231     return false;
232 }
233 } // namespace UpdateService
234 } // namespace OHOS
235