• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2023 Huawei Device Co., Ltd.
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 //     http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 use std::io::{self, SeekFrom};
15 use std::sync::atomic::{AtomicBool, AtomicI64, AtomicU32, AtomicU64, AtomicU8, Ordering};
16 use std::sync::{Arc, Mutex};
17 use std::time::Duration;
18 
19 use ylong_http_client::async_impl::{Body, Client, Request, RequestBuilder, Response};
20 use ylong_http_client::{ErrorKind, HttpClientError};
21 use ylong_runtime::io::{AsyncSeekExt, AsyncWriteExt};
22 
23 cfg_oh! {
24     use crate::manage::SystemConfig;
25 }
26 
27 use super::config::Version;
28 use super::info::{CommonTaskInfo, State, TaskInfo, UpdateInfo};
29 use super::notify::{EachFileStatus, NotifyData, Progress};
30 use super::reason::Reason;
31 use crate::error::ErrorCode;
32 use crate::manage::database::RequestDb;
33 use crate::manage::network_manager::NetworkManager;
34 use crate::manage::notifier::Notifier;
35 use crate::service::client::ClientManagerEntry;
36 use crate::service::notification_bar::NotificationDispatcher;
37 use crate::task::client::build_client;
38 use crate::task::config::{Action, TaskConfig};
39 use crate::task::files::{AttachedFiles, Files};
40 use crate::utils::form_item::FileSpec;
41 use crate::utils::get_current_timestamp;
42 
43 const RETRY_TIMES: u32 = 4;
44 const RETRY_INTERVAL: u64 = 400;
45 
46 pub(crate) struct RequestTask {
47     pub(crate) conf: TaskConfig,
48     pub(crate) client: Client,
49     pub(crate) files: Files,
50     pub(crate) body_files: Files,
51     pub(crate) ctime: u64,
52     pub(crate) mime_type: Mutex<String>,
53     pub(crate) progress: Mutex<Progress>,
54     pub(crate) status: Mutex<TaskStatus>,
55     pub(crate) code: Mutex<Vec<Reason>>,
56     pub(crate) tries: AtomicU32,
57     pub(crate) background_notify_time: AtomicU64,
58     pub(crate) background_notify: Arc<AtomicBool>,
59     pub(crate) file_total_size: AtomicI64,
60     pub(crate) rate_limiting: AtomicU64,
61     pub(crate) max_speed: AtomicI64,
62     pub(crate) last_notify: AtomicU64,
63     pub(crate) client_manager: ClientManagerEntry,
64     pub(crate) running_result: Mutex<Option<Result<(), Reason>>>,
65     pub(crate) timeout_tries: AtomicU32,
66     pub(crate) upload_resume: AtomicBool,
67     pub(crate) mode: AtomicU8,
68 }
69 
70 impl RequestTask {
task_id(&self) -> u3271     pub(crate) fn task_id(&self) -> u32 {
72         self.conf.common_data.task_id
73     }
74 
uid(&self) -> u6475     pub(crate) fn uid(&self) -> u64 {
76         self.conf.common_data.uid
77     }
78 
config(&self) -> &TaskConfig79     pub(crate) fn config(&self) -> &TaskConfig {
80         &self.conf
81     }
82 
83     // only use for download task
mime_type(&self) -> String84     pub(crate) fn mime_type(&self) -> String {
85         self.mime_type.lock().unwrap().clone()
86     }
87 
action(&self) -> Action88     pub(crate) fn action(&self) -> Action {
89         self.conf.common_data.action
90     }
91 
speed_limit(&self, limit: u64)92     pub(crate) fn speed_limit(&self, limit: u64) {
93         let old = self.rate_limiting.swap(limit, Ordering::SeqCst);
94         if old != limit {
95             info!("task {} speed_limit {}", self.task_id(), limit);
96         }
97     }
98 
network_retry(&self) -> Result<(), TaskError>99     pub(crate) async fn network_retry(&self) -> Result<(), TaskError> {
100         if self.tries.load(Ordering::SeqCst) < RETRY_TIMES {
101             self.tries.fetch_add(1, Ordering::SeqCst);
102             if !NetworkManager::is_online() {
103                 return Err(TaskError::Waiting(TaskPhase::NetworkOffline));
104             } else {
105                 ylong_runtime::time::sleep(Duration::from_millis(RETRY_INTERVAL)).await;
106                 return Err(TaskError::Waiting(TaskPhase::NeedRetry));
107             }
108         }
109         Ok(())
110     }
111 }
112 
change_upload_size(begins: u64, mut ends: i64, size: i64) -> i64113 pub(crate) fn change_upload_size(begins: u64, mut ends: i64, size: i64) -> i64 {
114     if ends < 0 || ends >= size {
115         ends = size - 1;
116     }
117     if begins as i64 > ends {
118         return size;
119     }
120     ends - begins as i64 + 1
121 }
122 
123 impl RequestTask {
new( config: TaskConfig, files: AttachedFiles, client: Client, client_manager: ClientManagerEntry, upload_resume: bool, ) -> RequestTask124     pub(crate) fn new(
125         config: TaskConfig,
126         files: AttachedFiles,
127         client: Client,
128         client_manager: ClientManagerEntry,
129         upload_resume: bool,
130     ) -> RequestTask {
131         let file_len = files.files.len();
132         let action = config.common_data.action;
133 
134         let file_total_size = match action {
135             Action::Upload => {
136                 let mut file_total_size = 0i64;
137                 // If the total size overflows, ignore it.
138                 for size in files.sizes.iter() {
139                     file_total_size += *size;
140                 }
141                 file_total_size
142             }
143             Action::Download => -1,
144             _ => unreachable!("Action::Any in RequestTask::new never reach"),
145         };
146 
147         let mut sizes = files.sizes.clone();
148 
149         if action == Action::Upload && config.common_data.index < sizes.len() as u32 {
150             sizes[config.common_data.index as usize] = change_upload_size(
151                 config.common_data.begins,
152                 config.common_data.ends,
153                 sizes[config.common_data.index as usize],
154             );
155         }
156 
157         let time = get_current_timestamp();
158         let status = TaskStatus::new(time);
159         let progress = Progress::new(sizes);
160         let mode = AtomicU8::new(config.common_data.mode.repr);
161 
162         RequestTask {
163             conf: config,
164             client,
165             files: files.files,
166             body_files: files.body_files,
167             ctime: time,
168             mime_type: Mutex::new(String::new()),
169             progress: Mutex::new(progress),
170             tries: AtomicU32::new(0),
171             status: Mutex::new(status),
172             code: Mutex::new(vec![Reason::Default; file_len]),
173             background_notify_time: AtomicU64::new(time),
174             background_notify: Arc::new(AtomicBool::new(false)),
175             file_total_size: AtomicI64::new(file_total_size),
176             rate_limiting: AtomicU64::new(0),
177             max_speed: AtomicI64::new(0),
178             last_notify: AtomicU64::new(time),
179             client_manager,
180             running_result: Mutex::new(None),
181             timeout_tries: AtomicU32::new(0),
182             upload_resume: AtomicBool::new(upload_resume),
183             mode,
184         }
185     }
186 
new_by_info( config: TaskConfig, #[cfg(feature = "oh")] system: SystemConfig, info: TaskInfo, client_manager: ClientManagerEntry, upload_resume: bool, ) -> Result<RequestTask, ErrorCode>187     pub(crate) fn new_by_info(
188         config: TaskConfig,
189         #[cfg(feature = "oh")] system: SystemConfig,
190         info: TaskInfo,
191         client_manager: ClientManagerEntry,
192         upload_resume: bool,
193     ) -> Result<RequestTask, ErrorCode> {
194         #[cfg(feature = "oh")]
195         let (files, client) = check_config(&config, system)?;
196         #[cfg(not(feature = "oh"))]
197         let (files, client) = check_config(&config)?;
198 
199         let file_len = files.files.len();
200         let action = config.common_data.action;
201         let time = get_current_timestamp();
202 
203         let file_total_size = match action {
204             Action::Upload => {
205                 let mut file_total_size = 0i64;
206                 // If the total size overflows, ignore it.
207                 for size in files.sizes.iter() {
208                     file_total_size += *size;
209                 }
210                 file_total_size
211             }
212             Action::Download => *info.progress.sizes.first().unwrap_or(&-1),
213             _ => unreachable!("Action::Any in RequestTask::new never reach"),
214         };
215 
216         // If `TaskInfo` is provided, use data of it.
217         let ctime = info.common_data.ctime;
218         let mime_type = info.mime_type.clone();
219         let tries = info.common_data.tries;
220         let status = TaskStatus {
221             mtime: time,
222             state: State::from(info.progress.common_data.state),
223             reason: Reason::from(info.common_data.reason),
224         };
225         let progress = info.progress;
226         let mode = AtomicU8::new(config.common_data.mode.repr);
227 
228         let mut task = RequestTask {
229             conf: config,
230             client,
231             files: files.files,
232             body_files: files.body_files,
233             ctime,
234             mime_type: Mutex::new(mime_type),
235             progress: Mutex::new(progress),
236             tries: AtomicU32::new(tries),
237             status: Mutex::new(status),
238             code: Mutex::new(vec![Reason::Default; file_len]),
239             background_notify_time: AtomicU64::new(time),
240             background_notify: Arc::new(AtomicBool::new(false)),
241             file_total_size: AtomicI64::new(file_total_size),
242             rate_limiting: AtomicU64::new(0),
243             max_speed: AtomicI64::new(info.max_speed),
244             last_notify: AtomicU64::new(time),
245             client_manager,
246             running_result: Mutex::new(None),
247             timeout_tries: AtomicU32::new(0),
248             upload_resume: AtomicBool::new(upload_resume),
249             mode,
250         };
251         let background_notify = NotificationDispatcher::get_instance().register_task(&task);
252         task.background_notify = background_notify;
253         Ok(task)
254     }
255 
build_notify_data(&self) -> NotifyData256     pub(crate) fn build_notify_data(&self) -> NotifyData {
257         let vec = self.get_each_file_status();
258         NotifyData {
259             bundle: self.conf.bundle.clone(),
260             // `unwrap` for propagating panics among threads.
261             progress: self.progress.lock().unwrap().clone(),
262             action: self.conf.common_data.action,
263             version: self.conf.version,
264             each_file_status: vec,
265             task_id: self.conf.common_data.task_id,
266             uid: self.conf.common_data.uid,
267         }
268     }
269 
update_progress_in_database(&self)270     pub(crate) fn update_progress_in_database(&self) {
271         let mtime = self.status.lock().unwrap().mtime;
272         let reason = self.status.lock().unwrap().reason;
273         let progress = self.progress.lock().unwrap().clone();
274         let update_info = UpdateInfo {
275             mtime,
276             reason: reason.repr,
277             progress,
278             tries: self.tries.load(Ordering::SeqCst),
279             mime_type: self.mime_type(),
280         };
281         RequestDb::get_instance().update_task(self.task_id(), update_info);
282     }
283 
build_request_builder(&self) -> Result<RequestBuilder, HttpClientError>284     pub(crate) fn build_request_builder(&self) -> Result<RequestBuilder, HttpClientError> {
285         use ylong_http_client::async_impl::PercentEncoder;
286 
287         let url = self.conf.url.clone();
288         let url = match PercentEncoder::encode(url.as_str()) {
289             Ok(value) => value,
290             Err(e) => {
291                 error!("url percent encoding error is {:?}", e);
292                 sys_event!(
293                     ExecFault,
294                     DfxCode::TASK_FAULT_03,
295                     &format!("url percent encoding error is {:?}", e)
296                 );
297                 return Err(e);
298             }
299         };
300 
301         let method = match self.conf.method.to_uppercase().as_str() {
302             "PUT" => "PUT",
303             "POST" => "POST",
304             "GET" => "GET",
305             _ => match self.conf.common_data.action {
306                 Action::Upload => {
307                     if self.conf.version == Version::API10 {
308                         "PUT"
309                     } else {
310                         "POST"
311                     }
312                 }
313                 Action::Download => "GET",
314                 _ => "",
315             },
316         };
317         let mut request = RequestBuilder::new().method(method).url(url.as_str());
318         for (key, value) in self.conf.headers.iter() {
319             request = request.header(key.as_str(), value.as_str());
320         }
321         Ok(request)
322     }
323 
clear_downloaded_file(&self) -> Result<(), std::io::Error>324     pub(crate) async fn clear_downloaded_file(&self) -> Result<(), std::io::Error> {
325         info!("task {} clear downloaded file", self.task_id());
326         let file = self.files.get_mut(0).unwrap();
327         file.set_len(0).await?;
328         file.seek(SeekFrom::Start(0)).await?;
329 
330         let mut progress_guard = self.progress.lock().unwrap();
331         progress_guard.common_data.total_processed = 0;
332         progress_guard.processed[0] = 0;
333 
334         Ok(())
335     }
336 
build_download_request(&self) -> Result<Request, TaskError>337     pub(crate) async fn build_download_request(&self) -> Result<Request, TaskError> {
338         let mut request_builder = self.build_request_builder()?;
339 
340         let file = self.files.get_mut(0).unwrap();
341 
342         let has_downloaded = file.metadata().await?.len();
343         let resume_download = has_downloaded > 0;
344         let require_range = self.require_range();
345 
346         let begins = self.conf.common_data.begins;
347         let ends = self.conf.common_data.ends;
348 
349         debug!(
350             "task {} build download request, resume_download: {}, require_range: {}",
351             self.task_id(),
352             resume_download,
353             require_range
354         );
355         match (resume_download, require_range) {
356             (true, false) => {
357                 let (builder, support_range) = self.support_range(request_builder);
358                 request_builder = builder;
359                 if support_range {
360                     request_builder =
361                         self.range_request(request_builder, begins + has_downloaded, ends);
362                 } else {
363                     self.clear_downloaded_file().await?;
364                 }
365             }
366             (false, true) => {
367                 request_builder = self.range_request(request_builder, begins, ends);
368             }
369             (true, true) => {
370                 let (builder, support_range) = self.support_range(request_builder);
371                 request_builder = builder;
372                 if support_range {
373                     request_builder =
374                         self.range_request(request_builder, begins + has_downloaded, ends);
375                 } else {
376                     return Err(TaskError::Failed(Reason::UnsupportedRangeRequest));
377                 }
378             }
379             (false, false) => {}
380         };
381 
382         let request = request_builder.body(Body::slice(self.conf.data.clone()))?;
383         Ok(request)
384     }
385 
range_request( &self, request_builder: RequestBuilder, begins: u64, ends: i64, ) -> RequestBuilder386     fn range_request(
387         &self,
388         request_builder: RequestBuilder,
389         begins: u64,
390         ends: i64,
391     ) -> RequestBuilder {
392         let range = if ends < 0 {
393             format!("bytes={begins}-")
394         } else {
395             format!("bytes={begins}-{ends}")
396         };
397         request_builder.header("Range", range.as_str())
398     }
399 
support_range(&self, mut request_builder: RequestBuilder) -> (RequestBuilder, bool)400     fn support_range(&self, mut request_builder: RequestBuilder) -> (RequestBuilder, bool) {
401         let progress_guard = self.progress.lock().unwrap();
402         let mut support_range = false;
403         if let Some(etag) = progress_guard.extras.get("etag") {
404             request_builder = request_builder.header("If-Range", etag.as_str());
405             support_range = true;
406         } else if let Some(last_modified) = progress_guard.extras.get("last-modified") {
407             request_builder = request_builder.header("If-Range", last_modified.as_str());
408             support_range = true;
409         }
410         if !support_range {
411             info!("task {} not support range", self.task_id());
412         }
413         (request_builder, support_range)
414     }
415 
get_file_info(&self, response: &Response) -> Result<(), TaskError>416     pub(crate) fn get_file_info(&self, response: &Response) -> Result<(), TaskError> {
417         let content_type = response.headers().get("content-type");
418         if let Some(mime_type) = content_type {
419             if let Ok(value) = mime_type.to_string() {
420                 *self.mime_type.lock().unwrap() = value;
421             }
422         }
423 
424         let content_length = response.headers().get("content-length");
425         if let Some(Ok(len)) = content_length.map(|v| v.to_string()) {
426             match len.parse::<i64>() {
427                 Ok(v) => {
428                     let mut progress = self.progress.lock().unwrap();
429                     progress.sizes = vec![v + progress.processed[0] as i64];
430                     self.file_total_size.store(v, Ordering::SeqCst);
431                     debug!("the download task content-length is {}", v);
432                 }
433                 Err(e) => {
434                     error!("convert string to i64 error: {:?}", e);
435                     sys_event!(
436                         ExecFault,
437                         DfxCode::TASK_FAULT_09,
438                         &format!("convert string to i64 error: {:?}", e)
439                     );
440                 }
441             }
442         } else {
443             error!("cannot get content-length of the task");
444             sys_event!(
445                 ExecFault,
446                 DfxCode::TASK_FAULT_09,
447                 "cannot get content-length of the task"
448             );
449             if self.conf.common_data.precise {
450                 return Err(TaskError::Failed(Reason::GetFileSizeFailed));
451             }
452         }
453         Ok(())
454     }
455 
handle_download_error( &self, err: HttpClientError, ) -> Result<(), TaskError>456     pub(crate) async fn handle_download_error(
457         &self,
458         err: HttpClientError,
459     ) -> Result<(), TaskError> {
460         if err.error_kind() != ErrorKind::UserAborted {
461             error!("Task {} {:?}", self.task_id(), err);
462         }
463         match err.error_kind() {
464             ErrorKind::Timeout => {
465                 sys_event!(
466                     ExecFault,
467                     DfxCode::TASK_FAULT_01,
468                     &format!("Task {} {:?}", self.task_id(), err)
469                 );
470                 Err(TaskError::Failed(Reason::ContinuousTaskTimeout))
471             }
472             // user triggered
473             ErrorKind::UserAborted => {
474                 sys_event!(
475                     ExecFault,
476                     DfxCode::TASK_FAULT_09,
477                     &format!("Task {} {:?}", self.task_id(), err)
478                 );
479                 Err(TaskError::Waiting(TaskPhase::UserAbort))
480             }
481             ErrorKind::BodyTransfer | ErrorKind::BodyDecode => {
482                 self.network_retry().await?;
483                 sys_event!(
484                     ExecFault,
485                     DfxCode::TASK_FAULT_09,
486                     &format!("Task {} {:?}", self.task_id(), err)
487                 );
488                 Err(TaskError::Failed(Reason::OthersError))
489             }
490             _ => {
491                 if format!("{}", err).contains("No space left on device") {
492                     sys_event!(
493                         ExecFault,
494                         DfxCode::TASK_FAULT_09,
495                         &format!("Task {} {:?}", self.task_id(), err)
496                     );
497                     Err(TaskError::Failed(Reason::InsufficientSpace))
498                 } else {
499                     sys_event!(
500                         ExecFault,
501                         DfxCode::TASK_FAULT_09,
502                         &format!("Task {} {:?}", self.task_id(), err)
503                     );
504                     Err(TaskError::Failed(Reason::OthersError))
505                 }
506             }
507         }
508     }
509 
510     #[cfg(feature = "oh")]
notify_response(&self, response: &Response)511     pub(crate) fn notify_response(&self, response: &Response) {
512         let tid = self.conf.common_data.task_id;
513         let version: String = response.version().as_str().into();
514         let status_code: u32 = response.status().as_u16() as u32;
515         let status_message: String;
516         if let Some(reason) = response.status().reason() {
517             status_message = reason.into();
518         } else {
519             error!("bad status_message {:?}", status_code);
520             sys_event!(
521                 ExecFault,
522                 DfxCode::TASK_FAULT_02,
523                 &format!("bad status_message {:?}", status_code)
524             );
525             return;
526         }
527         let headers = response.headers().clone();
528         debug!("notify_response");
529         self.client_manager
530             .send_response(tid, version, status_code, status_message, headers)
531     }
532 
require_range(&self) -> bool533     pub(crate) fn require_range(&self) -> bool {
534         self.conf.common_data.begins > 0 || self.conf.common_data.ends >= 0
535     }
536 
record_upload_response( &self, index: usize, response: Result<Response, HttpClientError>, )537     pub(crate) async fn record_upload_response(
538         &self,
539         index: usize,
540         response: Result<Response, HttpClientError>,
541     ) {
542         if let Ok(mut r) = response {
543             {
544                 let mut guard = self.progress.lock().unwrap();
545                 guard.extras.clear();
546                 for (k, v) in r.headers() {
547                     if let Ok(value) = v.to_string() {
548                         guard.extras.insert(k.to_string().to_lowercase(), value);
549                     }
550                 }
551             }
552 
553             let file = match self.body_files.get_mut(index) {
554                 Some(file) => file,
555                 None => return,
556             };
557             let _ = file.set_len(0).await;
558             loop {
559                 let mut buf = [0u8; 1024];
560                 let size = r.data(&mut buf).await;
561                 let size = match size {
562                     Ok(size) => size,
563                     Err(_e) => break,
564                 };
565 
566                 if size == 0 {
567                     break;
568                 }
569                 let _ = file.write_all(&buf[..size]).await;
570             }
571             // Makes sure all the data has been written to the target file.
572             let _ = file.sync_all().await;
573         }
574     }
575 
get_each_file_status(&self) -> Vec<EachFileStatus>576     pub(crate) fn get_each_file_status(&self) -> Vec<EachFileStatus> {
577         let mut vec = Vec::new();
578         // `unwrap` for propagating panics among threads.
579         let codes_guard = self.code.lock().unwrap();
580         for (i, file_spec) in self.conf.file_specs.iter().enumerate() {
581             let reason = *codes_guard.get(i).unwrap_or(&Reason::Default);
582             vec.push(EachFileStatus {
583                 path: file_spec.path.clone(),
584                 reason,
585                 message: reason.to_str().into(),
586             });
587         }
588         vec
589     }
590 
info(&self) -> TaskInfo591     pub(crate) fn info(&self) -> TaskInfo {
592         let status = self.status.lock().unwrap();
593         let progress = self.progress.lock().unwrap();
594         let mode = self.mode.load(Ordering::Acquire);
595         TaskInfo {
596             bundle: self.conf.bundle.clone(),
597             url: self.conf.url.clone(),
598             data: self.conf.data.clone(),
599             token: self.conf.token.clone(),
600             form_items: self.conf.form_items.clone(),
601             file_specs: self.conf.file_specs.clone(),
602             title: self.conf.title.clone(),
603             description: self.conf.description.clone(),
604             mime_type: {
605                 match self.conf.version {
606                     Version::API10 => match self.conf.common_data.action {
607                         Action::Download => match self.conf.headers.get("Content-Type") {
608                             None => "".into(),
609                             Some(v) => v.clone(),
610                         },
611                         Action::Upload => "multipart/form-data".into(),
612                         _ => "".into(),
613                     },
614                     Version::API9 => self.mime_type.lock().unwrap().clone(),
615                 }
616             },
617             progress: progress.clone(),
618             extras: progress.extras.clone(),
619             common_data: CommonTaskInfo {
620                 task_id: self.conf.common_data.task_id,
621                 uid: self.conf.common_data.uid,
622                 action: self.conf.common_data.action.repr,
623                 mode,
624                 ctime: self.ctime,
625                 mtime: status.mtime,
626                 reason: status.reason.repr,
627                 gauge: self.conf.common_data.gauge,
628                 retry: self.conf.common_data.retry,
629                 tries: self.tries.load(Ordering::SeqCst),
630                 version: self.conf.version as u8,
631                 priority: self.conf.common_data.priority,
632             },
633             max_speed: self.max_speed.load(Ordering::SeqCst),
634         }
635     }
636 
notify_header_receive(&self)637     pub(crate) fn notify_header_receive(&self) {
638         if self.conf.version == Version::API9 && self.conf.common_data.action == Action::Upload {
639             let notify_data = self.build_notify_data();
640 
641             Notifier::header_receive(&self.client_manager, notify_data);
642         }
643     }
644 }
645 
646 #[derive(Clone, Debug)]
647 pub(crate) struct TaskStatus {
648     pub(crate) mtime: u64,
649     pub(crate) state: State,
650     pub(crate) reason: Reason,
651 }
652 
653 impl TaskStatus {
new(mtime: u64) -> Self654     pub(crate) fn new(mtime: u64) -> Self {
655         TaskStatus {
656             mtime,
657             state: State::Initialized,
658             reason: Reason::Default,
659         }
660     }
661 }
662 
check_file_specs(file_specs: &[FileSpec]) -> bool663 fn check_file_specs(file_specs: &[FileSpec]) -> bool {
664     const EL1: &str = "/data/storage/el1/base/";
665     const EL2: &str = "/data/storage/el2/base/";
666     const EL5: &str = "/data/storage/el5/base/";
667 
668     let mut result = true;
669     for (idx, spec) in file_specs.iter().enumerate() {
670         let path = &spec.path;
671         if !spec.is_user_file
672             && !path.starts_with(EL1)
673             && !path.starts_with(EL2)
674             && !path.starts_with(EL5)
675         {
676             error!("File path invalid - path: {}, idx: {}", path, idx);
677             sys_event!(
678                 ExecFault,
679                 DfxCode::TASK_FAULT_09,
680                 &format!("File path invalid - path: {}, idx: {}", path, idx)
681             );
682             result = false;
683             break;
684         }
685     }
686 
687     result
688 }
689 
check_config( config: &TaskConfig, #[cfg(feature = "oh")] system: SystemConfig, ) -> Result<(AttachedFiles, Client), ErrorCode>690 pub(crate) fn check_config(
691     config: &TaskConfig,
692     #[cfg(feature = "oh")] system: SystemConfig,
693 ) -> Result<(AttachedFiles, Client), ErrorCode> {
694     if !check_file_specs(&config.file_specs) {
695         return Err(ErrorCode::Other);
696     }
697     let files = AttachedFiles::open(config).map_err(|_| ErrorCode::FileOperationErr)?;
698     #[cfg(feature = "oh")]
699     let client = build_client(config, system).map_err(|_| ErrorCode::Other)?;
700 
701     #[cfg(not(feature = "oh"))]
702     let client = build_client(config).map_err(|_| ErrorCode::Other)?;
703     Ok((files, client))
704 }
705 
706 impl From<HttpClientError> for TaskError {
from(_value: HttpClientError) -> Self707     fn from(_value: HttpClientError) -> Self {
708         TaskError::Failed(Reason::BuildRequestFailed)
709     }
710 }
711 
712 impl From<io::Error> for TaskError {
from(_value: io::Error) -> Self713     fn from(_value: io::Error) -> Self {
714         TaskError::Failed(Reason::IoError)
715     }
716 }
717 
718 #[derive(Debug, PartialEq, Eq)]
719 pub enum TaskPhase {
720     NeedRetry,
721     UserAbort,
722     NetworkOffline,
723 }
724 
725 #[derive(Debug, PartialEq, Eq)]
726 pub enum TaskError {
727     Failed(Reason),
728     Waiting(TaskPhase),
729 }
730 
731 #[cfg(test)]
732 mod test {
733     use crate::task::request_task::change_upload_size;
734 
735     #[test]
ut_upload_size()736     fn ut_upload_size() {
737         assert_eq!(change_upload_size(0, -1, 30), 30);
738         assert_eq!(change_upload_size(10, -1, 30), 20);
739         assert_eq!(change_upload_size(0, 10, 30), 11);
740         assert_eq!(change_upload_size(10, 10, 100), 1);
741         assert_eq!(change_upload_size(0, 30, 30), 30);
742         assert_eq!(change_upload_size(0, 0, 0), 0);
743         assert_eq!(change_upload_size(10, 9, 100), 100);
744     }
745 }
746