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::collections::HashMap;
15 use std::fmt::Display;
16 use std::mem::MaybeUninit;
17 use std::pin::Pin;
18 use std::sync::{Arc, Mutex, Once};
19 
20 pub(crate) use ffi::*;
21 
22 cfg_oh! {
23     use crate::manage::SystemConfig;
24 }
25 
26 cfg_not_oh! {
27     use rusqlite::Connection;
28     const CREATE_TABLE: &'static str = "CREATE TABLE IF NOT EXISTS request_task (task_id INTEGER PRIMARY KEY, uid INTEGER, token_id INTEGER, action INTEGER, mode INTEGER, cover INTEGER, network INTEGER, metered INTEGER, roaming INTEGER, ctime INTEGER, mtime INTEGER, reason INTEGER, gauge INTEGER, retry INTEGER, redirect INTEGER, tries INTEGER, version INTEGER, config_idx INTEGER, begins INTEGER, ends INTEGER, precise INTEGER, priority INTEGER, background INTEGER, bundle TEXT, url TEXT, data TEXT, token TEXT, title TEXT, description TEXT, method TEXT, headers TEXT, config_extras TEXT, mime_type TEXT, state INTEGER, idx INTEGER, total_processed INTEGER, sizes TEXT, processed TEXT, extras TEXT, form_items BLOB, file_specs BLOB, each_file_status BLOB, body_file_names BLOB, certs_paths BLOB)";
29 }
30 use crate::error::ErrorCode;
31 use crate::service::client::ClientManagerEntry;
32 use crate::task::config::TaskConfig;
33 use crate::task::ffi::{CEachFileStatus, CTaskConfig, CTaskInfo, CUpdateInfo};
34 use crate::task::info::{State, TaskInfo, UpdateInfo};
35 use crate::task::reason::Reason;
36 use crate::task::request_task::RequestTask;
37 use crate::utils::{get_current_timestamp, hashmap_to_string};
38 
39 pub(crate) struct RequestDb {
40     user_file_tasks: Mutex<HashMap<u32, Arc<RequestTask>>>,
41     #[cfg(feature = "oh")]
42     pub(crate) inner: *mut RequestDataBase,
43     #[cfg(not(feature = "oh"))]
44     pub(crate) inner: Connection,
45 }
46 
47 impl RequestDb {
48     #[cfg(feature = "oh")]
get_instance() -> &'static Self49     pub(crate) fn get_instance() -> &'static Self {
50         static mut DB: MaybeUninit<RequestDb> = MaybeUninit::uninit();
51         static ONCE: Once = Once::new();
52         ONCE.call_once(|| {
53             let (path, encrypt) = if cfg!(test) {
54                 ("/data/test/request.db", false)
55             } else {
56                 ("/data/service/el1/public/database/request/request.db", true)
57             };
58 
59             let inner = GetDatabaseInstance(path, encrypt);
60             unsafe {
61                 DB.write(RequestDb {
62                     inner,
63                     user_file_tasks: Mutex::new(HashMap::new()),
64                 });
65             }
66         });
67         unsafe { DB.assume_init_mut() }
68     }
69 
70     #[cfg(not(feature = "oh"))]
get_instance() -> &'static Self71     pub(crate) fn get_instance() -> &'static Self {
72         static mut DATABASE: MaybeUninit<RequestDb> = MaybeUninit::uninit();
73         static ONCE: Once = Once::new();
74         ONCE.call_once(|| {
75             let inner = Connection::open_in_memory().unwrap();
76             inner.execute(&CREATE_TABLE, ()).unwrap();
77             unsafe {
78                 DATABASE.write(RequestDb {
79                     inner,
80                     user_file_tasks: Mutex::new(HashMap::new()),
81                 })
82             };
83         });
84 
85         unsafe { DATABASE.assume_init_ref() }
86     }
87 
88     #[cfg(feature = "oh")]
execute(&self, sql: &str) -> Result<(), i32>89     pub(crate) fn execute(&self, sql: &str) -> Result<(), i32> {
90         let ret = unsafe { Pin::new_unchecked(&mut *self.inner).ExecuteSql(sql) };
91         if ret == 0 {
92             Ok(())
93         } else {
94             error!("execute {} failed: {}", sql, ret);
95             Err(ret)
96         }
97     }
98 
99     #[cfg(not(feature = "oh"))]
execute(&self, sql: &str) -> Result<(), i32>100     pub(crate) fn execute(&self, sql: &str) -> Result<(), i32> {
101         let res = self.inner.execute(sql, ());
102 
103         self.inner.execute(sql, ()).map(|_| ()).map_err(|e| {
104             error!("execute sql failed: {}", e);
105             e.sqlite_error_code().unwrap() as u32 as i32
106         })
107     }
108 
109     #[cfg(feature = "oh")]
query_integer<T: TryFrom<i64> + Default>(&self, sql: &str) -> Vec<T> where T::Error: Display,110     pub(crate) fn query_integer<T: TryFrom<i64> + Default>(&self, sql: &str) -> Vec<T>
111     where
112         T::Error: Display,
113     {
114         let mut v = vec![];
115         let ret = unsafe { Pin::new_unchecked(&mut *self.inner).QueryInteger(sql, &mut v) };
116         let v = v
117             .into_iter()
118             .map(|a| {
119                 a.try_into().unwrap_or_else(|e| {
120                     error!("query_integer failed, value: {}", e);
121                     Default::default()
122                 })
123             })
124             .collect();
125 
126         if ret != 0 {
127             error!("query integer err:{}", ret);
128         }
129         v
130     }
131 
132     #[cfg(not(feature = "oh"))]
query_integer<T: TryFrom<i64> + Default>(&self, sql: &str) -> Vec<T> where T::Error: Display,133     pub(crate) fn query_integer<T: TryFrom<i64> + Default>(&self, sql: &str) -> Vec<T>
134     where
135         T::Error: Display,
136     {
137         let mut stmt = self.inner.prepare(sql).unwrap();
138         let rows = stmt.query_map([], |row| Ok(row.get(0).unwrap())).unwrap();
139         let v: Vec<i64> = rows.into_iter().map(|a| a.unwrap()).collect();
140         v.into_iter()
141             .map(|a| a.try_into().unwrap_or_else(|_| Default::default()))
142             .collect()
143     }
144 
contains_task(&self, task_id: u32) -> bool145     pub(crate) fn contains_task(&self, task_id: u32) -> bool {
146         let sql = format!(
147             "SELECT COUNT(*) FROM request_task WHERE task_id = {}",
148             task_id
149         );
150         let v = self.query_integer::<u32>(&sql);
151 
152         if v.is_empty() {
153             error!("contains_task check failed, empty result");
154             false
155         } else {
156             v[0] == 1
157         }
158     }
159 
query_task_token_id(&self, task_id: u32) -> Result<u64, i32>160     pub(crate) fn query_task_token_id(&self, task_id: u32) -> Result<u64, i32> {
161         let sql = format!(
162             "SELECT token_id FROM request_task WHERE task_id = {}",
163             task_id
164         );
165         let v = self.query_integer::<u64>(&sql);
166         if v.is_empty() {
167             error!("query_task_token_id failed, empty result");
168             Err(-1)
169         } else {
170             Ok(v[0])
171         }
172     }
173 
174     #[cfg(feature = "oh")]
insert_task(&self, task: RequestTask) -> bool175     pub(crate) fn insert_task(&self, task: RequestTask) -> bool {
176         let task_id = task.task_id();
177         let uid = task.uid();
178 
179         debug!("Insert task to database, uid: {}, tid: {}", uid, task_id);
180 
181         if self.contains_task(task_id) {
182             return false;
183         }
184 
185         let task_config = task.config();
186         let config_set = task_config.build_config_set();
187         let c_task_config = task_config.to_c_struct(task_id, uid, &config_set);
188 
189         let task_info = &task.info();
190         let info_set = task_info.build_info_set();
191         let c_task_info = task_info.to_c_struct(&info_set);
192 
193         if !unsafe { RecordRequestTask(&c_task_info, &c_task_config) } {
194             info!("task {} insert database fail", task_id);
195         }
196 
197         // For some tasks contains user_file, we must save it to map first.
198         if task.conf.contains_user_file() {
199             self.user_file_tasks
200                 .lock()
201                 .unwrap()
202                 .insert(task.task_id(), Arc::new(task));
203         };
204         true
205     }
206 
207     #[cfg(not(feature = "oh"))]
insert_task(&self, task: RequestTask) -> bool208     pub(crate) fn insert_task(&self, task: RequestTask) -> bool {
209         use crate::task::reason::Reason;
210         use crate::utils::get_current_timestamp;
211 
212         let task_id = task.task_id();
213         let uid = task.uid();
214         info!("insert database, uid {} tid {}", uid, task_id);
215         if self.contains_task(task_id) {
216             return false;
217         }
218 
219         let config = task.config();
220         let sql = format!(
221             "INSERT OR REPLACE INTO request_task (task_id, uid, token_id, action, mode, cover, network, metered, roaming, ctime, gauge, retry, redirect, version, config_idx, begins, ends, precise, priority, background, bundle, url, data, token, title, description, method, headers, config_extras, mtime, reason, tries, state)
222             VALUES ({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', {}, {}, {}, {})",
223             config.common_data.task_id,
224             config.common_data.uid,
225             config.common_data.token_id,
226             config.common_data.action.repr,
227             config.common_data.mode.repr,
228             config.common_data.cover,
229             config.common_data.network_config as u8,
230             config.common_data.metered as u8,
231             config.common_data.roaming as u8,
232             get_current_timestamp(),
233             config.common_data.gauge,
234             config.common_data.retry,
235             config.common_data.redirect,
236             config.version as u8,
237             config.common_data.index,
238             config.common_data.begins,
239             config.common_data.ends,
240             config.common_data.precise,
241             config.common_data.priority,
242             config.common_data.background as u8,
243             config.bundle,
244             config.url,
245             config.data,
246             config.token,
247             config.title,
248             config.description,
249             config.method,
250             hashmap_to_string(&config.headers),
251             hashmap_to_string(&config.extras),
252             get_current_timestamp(),
253             Reason::Default.repr,
254             0,
255             State::Initialized.repr,
256         );
257         self.execute(&sql).unwrap();
258 
259         // For some tasks contains user_file, we must save it to map first.
260         if task.conf.contains_user_file() {
261             self.user_file_tasks
262                 .lock()
263                 .unwrap()
264                 .insert(task.task_id(), Arc::new(task));
265         };
266         true
267     }
268 
269     #[cfg(feature = "oh")]
update_task(&self, task_id: u32, update_info: UpdateInfo)270     pub(crate) fn update_task(&self, task_id: u32, update_info: UpdateInfo) {
271         debug!("Update task in database, task_id: {}", task_id);
272         if !self.contains_task(task_id) {
273             return;
274         }
275         let sizes = format!("{:?}", update_info.progress.sizes);
276         let processed = format!("{:?}", update_info.progress.processed);
277         let extras = hashmap_to_string(&update_info.progress.extras);
278         let each_file_status: Vec<CEachFileStatus> = update_info
279             .each_file_status
280             .iter()
281             .map(|x| x.to_c_struct())
282             .collect();
283         let c_update_info = update_info.to_c_struct(&sizes, &processed, &extras, &each_file_status);
284         let ret = unsafe { UpdateRequestTask(task_id, &c_update_info) };
285         debug!("Update task in database, ret is {}", ret);
286     }
287 
clear_invalid_records(&self)288     pub(crate) fn clear_invalid_records(&self) {
289         let sql = format!(
290             "UPDATE request_task SET state = {} WHERE state = {} AND reason = {}",
291             State::Failed.repr,
292             State::Waiting.repr,
293             Reason::Default.repr,
294         );
295         let _ = self.execute(&sql);
296     }
297 
query_task_uid(&self, task_id: u32) -> Option<u64>298     pub(crate) fn query_task_uid(&self, task_id: u32) -> Option<u64> {
299         let sql = format!("SELECT uid FROM request_task WHERE task_id = {}", task_id);
300         self.query_integer(&sql).first().copied()
301     }
302 
303     #[cfg(not(feature = "oh"))]
update_task(&self, task_id: u32, update_info: UpdateInfo)304     pub(crate) fn update_task(&self, task_id: u32, update_info: UpdateInfo) {
305         if !self.contains_task(task_id) {
306             return;
307         }
308         let sql = format!(
309             "UPDATE request_task SET sizes = {:?}, processed = {:?}, extras = {} WHERE task_id = {}",
310             update_info.progress.sizes, update_info.progress.processed, hashmap_to_string(&update_info.progress.extras),
311             task_id,
312         );
313         self.execute(&sql).unwrap();
314     }
315 
update_task_state(&self, task_id: u32, state: State, reason: Reason)316     pub(crate) fn update_task_state(&self, task_id: u32, state: State, reason: Reason) {
317         let sql = format!(
318             "UPDATE request_task SET state = {}, mtime = {}, reason = {} WHERE task_id = {}",
319             state.repr,
320             get_current_timestamp(),
321             reason.repr,
322             task_id
323         );
324         let _ = self.execute(&sql);
325     }
326 
update_task_sizes(&self, task_id: u32, sizes: &Vec<i64>)327     pub(crate) fn update_task_sizes(&self, task_id: u32, sizes: &Vec<i64>) {
328         let sql = format!(
329             "UPDATE request_task SET sizes = '{:?}' WHERE task_id = {}",
330             sizes, task_id
331         );
332         let _ = self.execute(&sql);
333     }
334 
335     #[cfg(feature = "oh")]
get_task_info(&self, task_id: u32) -> Option<TaskInfo>336     pub(crate) fn get_task_info(&self, task_id: u32) -> Option<TaskInfo> {
337         debug!("Get task info from database");
338         let c_task_info = unsafe { GetTaskInfo(task_id) };
339         if c_task_info.is_null() {
340             info!("No task found in database");
341             return None;
342         }
343         let c_task_info = unsafe { &*c_task_info };
344         let task_info = TaskInfo::from_c_struct(c_task_info);
345         unsafe { DeleteCTaskInfo(c_task_info) };
346         Some(task_info)
347     }
348 
349     #[cfg(not(feature = "oh"))]
get_task_info(&self, task_id: u32) -> Option<TaskInfo>350     pub(crate) fn get_task_info(&self, task_id: u32) -> Option<TaskInfo> {
351         use crate::info::CommonTaskInfo;
352         use crate::task::notify::Progress;
353 
354         let sql = format!("SELECT task_id, uid, action, mode, mtime, reason, gauge, retry, version, priority, ctime, tries, url, data, token, state, idx from request_task where task_id = {}", task_id);
355         let mut stmt = self.inner.prepare(&sql).unwrap();
356         let mut row = stmt
357             .query_map([], |row| {
358                 Ok(TaskInfo {
359                     common_data: CommonTaskInfo {
360                         task_id: row.get(0).unwrap(),
361                         uid: row.get(1).unwrap(),
362                         action: row.get(2).unwrap(),
363                         mode: row.get(3).unwrap(),
364                         mtime: row.get(4).unwrap(),
365                         reason: row.get(5).unwrap(),
366                         gauge: row.get(6).unwrap(),
367                         retry: row.get(7).unwrap(),
368                         version: row.get(8).unwrap(),
369                         priority: row.get(9).unwrap(),
370                         ctime: row.get(10).unwrap(),
371                         tries: row.get(11).unwrap(),
372                     },
373                     url: row.get(12).unwrap(),
374                     data: row.get(13).unwrap(),
375                     token: row.get(14).unwrap(),
376                     bundle: "".to_string(),
377                     title: "".to_string(),
378                     description: "".to_string(),
379                     mime_type: "".to_string(),
380                     extras: HashMap::new(),
381                     each_file_status: vec![],
382                     form_items: vec![],
383                     file_specs: vec![],
384                     progress: Progress::new(vec![]),
385                 })
386             })
387             .unwrap();
388         row.next().map(|info| info.unwrap())
389     }
390 
391     #[cfg(feature = "oh")]
get_task_config(&self, task_id: u32) -> Option<TaskConfig>392     pub(crate) fn get_task_config(&self, task_id: u32) -> Option<TaskConfig> {
393         debug!("query single task config in database");
394         let c_task_config = unsafe { QueryTaskConfig(task_id) };
395         if c_task_config.is_null() {
396             error!("can not find task in database, task id: {}", task_id);
397             None
398         } else {
399             let task_config = TaskConfig::from_c_struct(unsafe { &*c_task_config });
400             unsafe { DeleteCTaskConfig(c_task_config) };
401             Some(task_config)
402         }
403     }
404 
405     #[cfg(not(feature = "oh"))]
get_task_config(&self, task_id: u32) -> Option<TaskConfig>406     pub(crate) fn get_task_config(&self, task_id: u32) -> Option<TaskConfig> {
407         use crate::config::{Action, CommonTaskConfig, NetworkConfig};
408 
409         debug!("query single task config in database");
410         let sql = format!("SELECT url, title, description, method, data, token, version from request_task where task_id = {}", task_id);
411         let mut stmt = self.inner.prepare(&sql).unwrap();
412         let mut row = stmt
413             .query_map([], |row| {
414                 let version: u8 = row.get(6).unwrap();
415                 Ok(TaskConfig {
416                     url: row.get(0).unwrap(),
417                     title: row.get(1).unwrap(),
418                     description: row.get(2).unwrap(),
419                     method: row.get(3).unwrap(),
420                     data: row.get(4).unwrap(),
421                     token: row.get(5).unwrap(),
422                     version: version.into(),
423                     common_data: CommonTaskConfig {
424                         task_id,
425                         uid: 0,
426                         token_id: 0,
427                         action: Action::Download,
428                         mode: Mode::BackGround,
429                         cover: true,
430                         network_config: NetworkConfig::Any,
431                         metered: true,
432                         roaming: true,
433                         gauge: true,
434                         retry: true,
435                         redirect: true,
436                         index: 0,
437                         begins: 0,
438                         ends: 0,
439                         precise: true,
440                         priority: 0,
441                         background: true,
442                     },
443                     headers: Default::default(),
444                     extras: Default::default(),
445                     form_items: Default::default(),
446                     file_specs: Default::default(),
447                     bundle: Default::default(),
448                     bundle_type: 0,
449                     body_file_paths: vec![],
450                     certs_path: vec![],
451                     proxy: Default::default(),
452                     certificate_pins: Default::default(),
453                     atomic_account: Default::default(),
454                 })
455             })
456             .unwrap();
457         row.next().map(|config| config.unwrap())
458     }
459 
460     /// Removes task records from a week ago before unloading.
delete_early_records(&self)461     pub(crate) fn delete_early_records(&self) {
462         use std::time::{SystemTime, UNIX_EPOCH};
463 
464         const MILLIS_IN_A_WEEK: u64 = 7 * 24 * 60 * 60 * 1000;
465         if let Ok(time) = SystemTime::now().duration_since(UNIX_EPOCH) {
466             let sql = format!(
467                 "DELETE from request_task WHERE mtime < {} ",
468                 time.as_millis() as u64 - MILLIS_IN_A_WEEK
469             );
470             let _ = self.execute(&sql);
471         }
472     }
473 
474     #[cfg(feature = "oh")]
get_task_qos_info(&self, task_id: u32) -> Option<TaskQosInfo>475     pub(crate) fn get_task_qos_info(&self, task_id: u32) -> Option<TaskQosInfo> {
476         #[cfg(feature = "oh")]
477         {
478             let mut info = TaskQosInfo {
479                 task_id,
480                 action: 0,
481                 mode: 0,
482                 state: 0,
483                 priority: 0,
484             };
485             let sql = format!(
486                 "SELECT action, mode, state, priority FROM request_task WHERE task_id = {}",
487                 task_id
488             );
489             let ret =
490                 unsafe { Pin::new_unchecked(&mut *self.inner).GetTaskQosInfo(&sql, &mut info) };
491             if ret == 0 {
492                 Some(info)
493             } else {
494                 None
495             }
496         }
497     }
498 
499     #[cfg(not(feature = "oh"))]
get_task_qos_info(&self, task_id: u32) -> Option<TaskQosInfo>500     pub(crate) fn get_task_qos_info(&self, task_id: u32) -> Option<TaskQosInfo> {
501         let sql = format!(
502             "SELECT action, mode, state, priority FROM request_task WHERE task_id = {}",
503             task_id,
504         );
505         let mut stmt = self.inner.prepare(&sql).unwrap();
506         let mut rows = stmt
507             .query_map([], |row| {
508                 Ok(TaskQosInfo {
509                     task_id: task_id,
510                     action: row.get::<_, u8>(0).unwrap().into(),
511                     mode: row.get::<_, u8>(1).unwrap().into(),
512                     state: row.get(2).unwrap(),
513                     priority: row.get(3).unwrap(),
514                 })
515             })
516             .unwrap();
517         rows.next().map(|info| info.unwrap())
518     }
519 
get_app_task_qos_infos_inner(&self, sql: &str) -> Vec<TaskQosInfo>520     pub(crate) fn get_app_task_qos_infos_inner(&self, sql: &str) -> Vec<TaskQosInfo> {
521         #[cfg(feature = "oh")]
522         {
523             let mut v = vec![];
524             let _ = unsafe { Pin::new_unchecked(&mut *self.inner).GetAppTaskQosInfos(sql, &mut v) };
525             v
526         }
527         #[cfg(not(feature = "oh"))]
528         {
529             let mut stmt = self.inner.prepare(&sql).unwrap();
530             let rows = stmt
531                 .query_map([], |row| {
532                     Ok(TaskQosInfo {
533                         task_id: row.get(0).unwrap(),
534                         action: row.get::<_, u8>(1).unwrap().into(),
535                         mode: row.get::<_, u8>(2).unwrap().into(),
536                         state: row.get(3).unwrap(),
537                         priority: row.get(4).unwrap(),
538                     })
539                 })
540                 .unwrap();
541             rows.into_iter().map(|info| info.unwrap()).collect()
542         }
543     }
544 
get_app_task_qos_infos(&self, uid: u64) -> Vec<TaskQosInfo>545     pub(crate) fn get_app_task_qos_infos(&self, uid: u64) -> Vec<TaskQosInfo> {
546         let sql = format!(
547             "SELECT task_id, action, mode, state, priority FROM request_task WHERE uid = {} AND ((state = {} AND reason = {}) OR state = {} OR state = {})",
548             uid,
549             State::Waiting.repr,
550             Reason::RunningTaskMeetLimits.repr,
551             State::Running.repr,
552             State::Retrying.repr,
553         );
554         self.get_app_task_qos_infos_inner(&sql)
555     }
556 
get_task( &self, task_id: u32, #[cfg(feature = "oh")] system: SystemConfig, client_manager: &ClientManagerEntry, upload_resume: bool, ) -> Result<Arc<RequestTask>, ErrorCode>557     pub(crate) fn get_task(
558         &self,
559         task_id: u32,
560         #[cfg(feature = "oh")] system: SystemConfig,
561         client_manager: &ClientManagerEntry,
562         upload_resume: bool,
563     ) -> Result<Arc<RequestTask>, ErrorCode> {
564         // If this task exists in `user_file_map`,get it from this map.
565         if let Some(task) = self.user_file_tasks.lock().unwrap().get(&task_id) {
566             return Ok(task.clone());
567         }
568 
569         // 此处需要根据 task_id 从数据库构造指定的任务。
570         let config = match self.get_task_config(task_id) {
571             Some(config) => config,
572             None => return Err(ErrorCode::TaskNotFound),
573         };
574         let task_id = config.common_data.task_id;
575 
576         let task_info = match self.get_task_info(task_id) {
577             Some(info) => info,
578             None => return Err(ErrorCode::TaskNotFound),
579         };
580 
581         let state = State::from(task_info.progress.common_data.state);
582         debug!("get_task {} state is {:?}", task_id, state);
583         if state == State::Removed {
584             error!("get_task state is Removed, {}", task_id);
585             return Err(ErrorCode::TaskStateErr);
586         }
587 
588         match RequestTask::new_by_info(
589             config,
590             #[cfg(feature = "oh")]
591             system,
592             task_info,
593             client_manager.clone(),
594             upload_resume,
595         ) {
596             Ok(task) => Ok(Arc::new(task)),
597             Err(e) => {
598                 error!("new RequestTask failed {}, err: {:?}", task_id, e);
599                 Err(e)
600             }
601         }
602     }
603 }
604 
605 unsafe impl Send for RequestDb {}
606 unsafe impl Sync for RequestDb {}
607 
608 #[cfg(feature = "oh")]
609 
610 extern "C" {
DeleteCTaskConfig(ptr: *const CTaskConfig)611     fn DeleteCTaskConfig(ptr: *const CTaskConfig);
DeleteCTaskInfo(ptr: *const CTaskInfo)612     fn DeleteCTaskInfo(ptr: *const CTaskInfo);
GetTaskInfo(task_id: u32) -> *const CTaskInfo613     fn GetTaskInfo(task_id: u32) -> *const CTaskInfo;
QueryTaskConfig(task_id: u32) -> *const CTaskConfig614     fn QueryTaskConfig(task_id: u32) -> *const CTaskConfig;
RecordRequestTask(info: *const CTaskInfo, config: *const CTaskConfig) -> bool615     fn RecordRequestTask(info: *const CTaskInfo, config: *const CTaskConfig) -> bool;
UpdateRequestTask(id: u32, info: *const CUpdateInfo) -> bool616     fn UpdateRequestTask(id: u32, info: *const CUpdateInfo) -> bool;
617 }
618 
619 #[cxx::bridge(namespace = "OHOS::Request")]
620 mod ffi {
621     #[derive(Clone, Debug, Copy)]
622     pub(crate) struct TaskQosInfo {
623         pub(crate) task_id: u32,
624         pub(crate) action: u8,
625         pub(crate) mode: u8,
626         pub(crate) state: u8,
627         pub(crate) priority: u32,
628     }
629 
630     unsafe extern "C++" {
631         include!("c_request_database.h");
632         type RequestDataBase;
GetDatabaseInstance(path: &str, encrypt: bool) -> *mut RequestDataBase633         fn GetDatabaseInstance(path: &str, encrypt: bool) -> *mut RequestDataBase;
ExecuteSql(self: Pin<&mut RequestDataBase>, sql: &str) -> i32634         fn ExecuteSql(self: Pin<&mut RequestDataBase>, sql: &str) -> i32;
QueryInteger(self: Pin<&mut RequestDataBase>, sql: &str, v: &mut Vec<i64>) -> i32635         fn QueryInteger(self: Pin<&mut RequestDataBase>, sql: &str, v: &mut Vec<i64>) -> i32;
GetAppTaskQosInfos( self: Pin<&mut RequestDataBase>, sql: &str, v: &mut Vec<TaskQosInfo>, ) -> i32636         fn GetAppTaskQosInfos(
637             self: Pin<&mut RequestDataBase>,
638             sql: &str,
639             v: &mut Vec<TaskQosInfo>,
640         ) -> i32;
GetTaskQosInfo(self: Pin<&mut RequestDataBase>, sql: &str, res: &mut TaskQosInfo) -> i32641         fn GetTaskQosInfo(self: Pin<&mut RequestDataBase>, sql: &str, res: &mut TaskQosInfo)
642             -> i32;
643     }
644 }
645 
646 #[cfg(feature = "oh")]
647 #[cfg(test)]
648 mod test {
649     use super::RequestDb;
650     use crate::config::{Action, Mode};
651     use crate::task::info::State;
652     use crate::tests::{lock_database, test_init};
653     use crate::utils::get_current_timestamp;
654     use crate::utils::task_id_generator::TaskIdGenerator;
655 
656     #[test]
ut_database_base()657     fn ut_database_base() {
658         test_init();
659         let _lock = lock_database();
660 
661         let task_id = TaskIdGenerator::generate();
662         let db = RequestDb::get_instance();
663         db.execute(&format!(
664             "INSERT INTO request_task (task_id, bundle) VALUES ({}, 'example_bundle')",
665             task_id
666         ))
667         .unwrap();
668 
669         let tasks =
670             db.query_integer("SELECT task_id FROM request_task WHERE bundle = 'example_bundle'");
671         assert!(tasks.contains(&task_id));
672     }
673 
674     #[test]
ut_database_contains_task()675     fn ut_database_contains_task() {
676         test_init();
677         let _lock = lock_database();
678         let task_id = TaskIdGenerator::generate();
679         let db = RequestDb::get_instance();
680         db.execute(&format!(
681             "INSERT INTO request_task (task_id, bundle) VALUES ({}, 'example_bundle')",
682             task_id
683         ))
684         .unwrap();
685 
686         assert!(db.contains_task(task_id));
687     }
688 
689     #[test]
ut_database_query_task_token_id()690     fn ut_database_query_task_token_id() {
691         test_init();
692         let _lock = lock_database();
693 
694         let task_id = TaskIdGenerator::generate();
695         let token_id = 123456789;
696         let db = RequestDb::get_instance();
697         db.execute(&format!(
698             "INSERT INTO request_task (task_id, token_id) VALUES ({}, {})",
699             task_id, token_id
700         ))
701         .unwrap();
702 
703         assert_eq!(db.query_task_token_id(task_id).unwrap(), token_id);
704     }
705 
706     #[test]
ut_database_app_task_qos_info()707     fn ut_database_app_task_qos_info() {
708         test_init();
709         let _lock = lock_database();
710         let task_id = TaskIdGenerator::generate();
711         let db = RequestDb::get_instance();
712         let priority = get_current_timestamp() as u32;
713         db.execute(&format!(
714             "INSERT INTO request_task (task_id, action, mode, state, priority) VALUES ({}, {}, {}, {}, {})",
715             task_id,
716             Action::Download.repr,
717             Mode::FrontEnd.repr,
718             State::Completed.repr,
719             priority,
720         ))
721         .unwrap();
722 
723         let info = db.get_task_qos_info(task_id).unwrap();
724         assert_eq!(info.task_id, task_id);
725         assert_eq!(info.action, Action::Download.repr);
726         assert_eq!(info.mode, Mode::FrontEnd.repr);
727         assert_eq!(info.state, State::Completed.repr);
728         assert_eq!(info.priority, priority);
729     }
730 }
731