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