• 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::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::{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         if v.is_empty() {
152             error!("contains_task check failed, empty result");
153             false
154         } else {
155             v[0] == 1
156         }
157     }
158 
query_task_token_id(&self, task_id: u32) -> Result<u64, i32>159     pub(crate) fn query_task_token_id(&self, task_id: u32) -> Result<u64, i32> {
160         let sql = format!(
161             "SELECT token_id FROM request_task WHERE task_id = {}",
162             task_id
163         );
164         let v = self.query_integer::<u64>(&sql);
165         if v.is_empty() {
166             error!("query_task_token_id failed, empty result");
167             Err(-1)
168         } else {
169             Ok(v[0])
170         }
171     }
172 
173     #[cfg(feature = "oh")]
insert_task(&self, task: RequestTask) -> bool174     pub(crate) fn insert_task(&self, task: RequestTask) -> bool {
175         let task_id = task.task_id();
176         let uid = task.uid();
177 
178         debug!("Insert task to database, uid: {}, tid: {}", uid, task_id);
179 
180         if self.contains_task(task_id) {
181             return false;
182         }
183 
184         let task_config = task.config();
185         let config_set = task_config.build_config_set();
186         let c_task_config = task_config.to_c_struct(task_id, uid, &config_set);
187 
188         let task_info = &task.info();
189         let info_set = task_info.build_info_set();
190         let c_task_info = task_info.to_c_struct(&info_set);
191 
192         if !unsafe { RecordRequestTask(&c_task_info, &c_task_config) } {
193             info!("task {} insert database fail", task_id);
194         }
195 
196         // For some tasks contains user_file, we must save it to map first.
197         if task.conf.contains_user_file() {
198             self.user_file_tasks
199                 .lock()
200                 .unwrap()
201                 .insert(task.task_id(), Arc::new(task));
202         };
203         true
204     }
205 
206     #[cfg(not(feature = "oh"))]
insert_task(&self, task: RequestTask) -> bool207     pub(crate) fn insert_task(&self, task: RequestTask) -> bool {
208         use crate::task::reason::Reason;
209         use crate::utils::get_current_timestamp;
210 
211         let task_id = task.task_id();
212         let uid = task.uid();
213         info!("insert database, uid {} tid {}", uid, task_id);
214         if self.contains_task(task_id) {
215             return false;
216         }
217 
218         let config = task.config();
219         let sql = format!(
220             "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)
221             VALUES ({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', {}, {}, {}, {})",
222             config.common_data.task_id,
223             config.common_data.uid,
224             config.common_data.token_id,
225             config.common_data.action.repr,
226             config.common_data.mode.repr,
227             config.common_data.cover,
228             config.common_data.network_config as u8,
229             config.common_data.metered as u8,
230             config.common_data.roaming as u8,
231             get_current_timestamp(),
232             config.common_data.gauge,
233             config.common_data.retry,
234             config.common_data.redirect,
235             config.version as u8,
236             config.common_data.index,
237             config.common_data.begins,
238             config.common_data.ends,
239             config.common_data.precise,
240             config.common_data.priority,
241             config.common_data.background as u8,
242             config.bundle,
243             config.url,
244             config.data,
245             config.token,
246             config.title,
247             config.description,
248             config.method,
249             hashmap_to_string(&config.headers),
250             hashmap_to_string(&config.extras),
251             get_current_timestamp(),
252             Reason::Default.repr,
253             0,
254             State::Initialized.repr,
255         );
256         self.execute(&sql).unwrap();
257 
258         // For some tasks contains user_file, we must save it to map first.
259         if task.conf.contains_user_file() {
260             self.user_file_tasks
261                 .lock()
262                 .unwrap()
263                 .insert(task.task_id(), Arc::new(task));
264         };
265         true
266     }
267 
268     #[cfg(feature = "oh")]
update_task(&self, task_id: u32, update_info: UpdateInfo)269     pub(crate) fn update_task(&self, task_id: u32, update_info: UpdateInfo) {
270         debug!("Update task in database, task_id: {}", task_id);
271         if !self.contains_task(task_id) {
272             return;
273         }
274         let sizes = format!("{:?}", update_info.progress.sizes);
275         let processed = format!("{:?}", update_info.progress.processed);
276         let extras = hashmap_to_string(&update_info.progress.extras);
277         let c_update_info = update_info.to_c_struct(&sizes, &processed, &extras);
278         let ret = unsafe { UpdateRequestTask(task_id, &c_update_info) };
279         debug!("Update task in database, ret is {}", ret);
280     }
281 
clear_invalid_records(&self)282     pub(crate) fn clear_invalid_records(&self) {
283         let sql = format!(
284             "UPDATE request_task SET state = {} WHERE state = {} AND reason = {}",
285             State::Failed.repr,
286             State::Waiting.repr,
287             Reason::Default.repr,
288         );
289         let _ = self.execute(&sql);
290     }
291 
query_task_uid(&self, task_id: u32) -> Option<u64>292     pub(crate) fn query_task_uid(&self, task_id: u32) -> Option<u64> {
293         let sql = format!("SELECT uid FROM request_task WHERE task_id = {}", task_id);
294         self.query_integer(&sql).first().copied()
295     }
296 
297     #[cfg(not(feature = "oh"))]
update_task(&self, task_id: u32, update_info: UpdateInfo)298     pub(crate) fn update_task(&self, task_id: u32, update_info: UpdateInfo) {
299         if !self.contains_task(task_id) {
300             return;
301         }
302         let sql = format!(
303             "UPDATE request_task SET sizes = {:?}, processed = {:?}, extras = {} WHERE task_id = {}",
304             update_info.progress.sizes, update_info.progress.processed, hashmap_to_string(&update_info.progress.extras),
305             task_id,
306         );
307         self.execute(&sql).unwrap();
308     }
309 
update_task_state(&self, task_id: u32, state: State, reason: Reason)310     pub(crate) fn update_task_state(&self, task_id: u32, state: State, reason: Reason) {
311         let sql = format!(
312             "UPDATE request_task SET state = {}, mtime = {}, reason = {} WHERE task_id = {}",
313             state.repr,
314             get_current_timestamp(),
315             reason.repr,
316             task_id
317         );
318         let _ = self.execute(&sql);
319     }
320 
update_task_sizes(&self, task_id: u32, sizes: &Vec<i64>)321     pub(crate) fn update_task_sizes(&self, task_id: u32, sizes: &Vec<i64>) {
322         let sql = format!(
323             "UPDATE request_task SET sizes = '{:?}' WHERE task_id = {}",
324             sizes, task_id
325         );
326         let _ = self.execute(&sql);
327     }
328 
329     #[cfg(feature = "oh")]
get_task_info(&self, task_id: u32) -> Option<TaskInfo>330     pub(crate) fn get_task_info(&self, task_id: u32) -> Option<TaskInfo> {
331         debug!("Get task info from database");
332         let c_task_info = unsafe { GetTaskInfo(task_id) };
333         if c_task_info.is_null() {
334             info!("No task found in database");
335             return None;
336         }
337         let c_task_info = unsafe { &*c_task_info };
338         let task_info = TaskInfo::from_c_struct(c_task_info);
339         unsafe { DeleteCTaskInfo(c_task_info) };
340         Some(task_info)
341     }
342 
query_task_total_processed(&self, task_id: u32) -> Option<i64>343     pub(crate) fn query_task_total_processed(&self, task_id: u32) -> Option<i64> {
344         let sql = format!(
345             "SELECT total_processed FROM request_task WHERE task_id = {}",
346             task_id
347         );
348         self.query_integer(&sql).first().copied()
349     }
350 
query_task_state(&self, task_id: u32) -> Option<u8>351     pub(crate) fn query_task_state(&self, task_id: u32) -> Option<u8> {
352         let sql = format!("SELECT state FROM request_task WHERE task_id = {}", task_id);
353         self.query_integer(&sql)
354             .first()
355             .map(|state: &i32| *state as u8)
356     }
357 
358     #[cfg(not(feature = "oh"))]
get_task_info(&self, task_id: u32) -> Option<TaskInfo>359     pub(crate) fn get_task_info(&self, task_id: u32) -> Option<TaskInfo> {
360         use crate::info::CommonTaskInfo;
361         use crate::task::notify::Progress;
362 
363         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);
364         let mut stmt = self.inner.prepare(&sql).unwrap();
365         let mut row = stmt
366             .query_map([], |row| {
367                 Ok(TaskInfo {
368                     common_data: CommonTaskInfo {
369                         task_id: row.get(0).unwrap(),
370                         uid: row.get(1).unwrap(),
371                         action: row.get(2).unwrap(),
372                         mode: row.get(3).unwrap(),
373                         mtime: row.get(4).unwrap(),
374                         reason: row.get(5).unwrap(),
375                         gauge: row.get(6).unwrap(),
376                         retry: row.get(7).unwrap(),
377                         version: row.get(8).unwrap(),
378                         priority: row.get(9).unwrap(),
379                         ctime: row.get(10).unwrap(),
380                         tries: row.get(11).unwrap(),
381                     },
382                     url: row.get(12).unwrap(),
383                     data: row.get(13).unwrap(),
384                     token: row.get(14).unwrap(),
385                     bundle: "".to_string(),
386                     title: "".to_string(),
387                     description: "".to_string(),
388                     mime_type: "".to_string(),
389                     extras: HashMap::new(),
390                     each_file_status: vec![],
391                     form_items: vec![],
392                     file_specs: vec![],
393                     progress: Progress::new(vec![]),
394                 })
395             })
396             .unwrap();
397         row.next().map(|info| info.unwrap())
398     }
399 
400     #[cfg(feature = "oh")]
get_task_config(&self, task_id: u32) -> Option<TaskConfig>401     pub(crate) fn get_task_config(&self, task_id: u32) -> Option<TaskConfig> {
402         debug!("query single task config in database");
403         let c_task_config = unsafe { QueryTaskConfig(task_id) };
404         if c_task_config.is_null() {
405             error!("can not find task in database, task id: {}", task_id);
406             None
407         } else {
408             let task_config = TaskConfig::from_c_struct(unsafe { &*c_task_config });
409             unsafe { DeleteCTaskConfig(c_task_config) };
410             Some(task_config)
411         }
412     }
413 
414     #[cfg(not(feature = "oh"))]
get_task_config(&self, task_id: u32) -> Option<TaskConfig>415     pub(crate) fn get_task_config(&self, task_id: u32) -> Option<TaskConfig> {
416         use crate::config::{Action, CommonTaskConfig, NetworkConfig};
417 
418         debug!("query single task config in database");
419         let sql = format!("SELECT url, title, description, method, data, token, version from request_task where task_id = {}", task_id);
420         let mut stmt = self.inner.prepare(&sql).unwrap();
421         let mut row = stmt
422             .query_map([], |row| {
423                 let version: u8 = row.get(6).unwrap();
424                 Ok(TaskConfig {
425                     url: row.get(0).unwrap(),
426                     title: row.get(1).unwrap(),
427                     description: row.get(2).unwrap(),
428                     method: row.get(3).unwrap(),
429                     data: row.get(4).unwrap(),
430                     token: row.get(5).unwrap(),
431                     version: version.into(),
432                     common_data: CommonTaskConfig {
433                         task_id,
434                         uid: 0,
435                         token_id: 0,
436                         action: Action::Download,
437                         mode: Mode::BackGround,
438                         cover: true,
439                         network_config: NetworkConfig::Any,
440                         metered: true,
441                         roaming: true,
442                         gauge: true,
443                         retry: true,
444                         redirect: true,
445                         index: 0,
446                         begins: 0,
447                         ends: 0,
448                         precise: true,
449                         priority: 0,
450                         background: true,
451                         multipart: false,
452                     },
453                     headers: Default::default(),
454                     extras: Default::default(),
455                     form_items: Default::default(),
456                     file_specs: Default::default(),
457                     bundle: Default::default(),
458                     bundle_type: 0,
459                     body_file_paths: vec![],
460                     certs_path: vec![],
461                     proxy: Default::default(),
462                     certificate_pins: Default::default(),
463                     atomic_account: Default::default(),
464                 })
465             })
466             .unwrap();
467         row.next().map(|config| config.unwrap())
468     }
469 
470     #[cfg(feature = "oh")]
get_task_qos_info(&self, task_id: u32) -> Option<TaskQosInfo>471     pub(crate) fn get_task_qos_info(&self, task_id: u32) -> Option<TaskQosInfo> {
472         #[cfg(feature = "oh")]
473         {
474             let mut info = TaskQosInfo {
475                 task_id,
476                 action: 0,
477                 mode: 0,
478                 state: 0,
479                 priority: 0,
480             };
481             let sql = format!(
482                 "SELECT action, mode, state, priority FROM request_task WHERE task_id = {}",
483                 task_id
484             );
485             let ret =
486                 unsafe { Pin::new_unchecked(&mut *self.inner).GetTaskQosInfo(&sql, &mut info) };
487             if ret == 0 {
488                 Some(info)
489             } else {
490                 None
491             }
492         }
493     }
494 
495     #[cfg(not(feature = "oh"))]
get_task_qos_info(&self, task_id: u32) -> Option<TaskQosInfo>496     pub(crate) fn get_task_qos_info(&self, task_id: u32) -> Option<TaskQosInfo> {
497         let sql = format!(
498             "SELECT action, mode, state, priority FROM request_task WHERE task_id = {}",
499             task_id,
500         );
501         let mut stmt = self.inner.prepare(&sql).unwrap();
502         let mut rows = stmt
503             .query_map([], |row| {
504                 Ok(TaskQosInfo {
505                     task_id: task_id,
506                     action: row.get::<_, u8>(0).unwrap().into(),
507                     mode: row.get::<_, u8>(1).unwrap().into(),
508                     state: row.get(2).unwrap(),
509                     priority: row.get(3).unwrap(),
510                 })
511             })
512             .unwrap();
513         rows.next().map(|info| info.unwrap())
514     }
515 
get_app_task_qos_infos_inner(&self, sql: &str) -> Vec<TaskQosInfo>516     pub(crate) fn get_app_task_qos_infos_inner(&self, sql: &str) -> Vec<TaskQosInfo> {
517         #[cfg(feature = "oh")]
518         {
519             let mut v = vec![];
520             let _ = unsafe { Pin::new_unchecked(&mut *self.inner).GetAppTaskQosInfos(sql, &mut v) };
521             v
522         }
523         #[cfg(not(feature = "oh"))]
524         {
525             let mut stmt = self.inner.prepare(&sql).unwrap();
526             let rows = stmt
527                 .query_map([], |row| {
528                     Ok(TaskQosInfo {
529                         task_id: row.get(0).unwrap(),
530                         action: row.get::<_, u8>(1).unwrap().into(),
531                         mode: row.get::<_, u8>(2).unwrap().into(),
532                         state: row.get(3).unwrap(),
533                         priority: row.get(4).unwrap(),
534                     })
535                 })
536                 .unwrap();
537             rows.into_iter().map(|info| info.unwrap()).collect()
538         }
539     }
540 
get_app_task_qos_infos(&self, uid: u64) -> Vec<TaskQosInfo>541     pub(crate) fn get_app_task_qos_infos(&self, uid: u64) -> Vec<TaskQosInfo> {
542         let sql = format!(
543             "SELECT task_id, action, mode, state, priority FROM request_task WHERE uid = {} AND ((state = {} AND reason = {}) OR state = {} OR state = {})",
544             uid,
545             State::Waiting.repr,
546             Reason::RunningTaskMeetLimits.repr,
547             State::Running.repr,
548             State::Retrying.repr,
549         );
550         self.get_app_task_qos_infos_inner(&sql)
551     }
552 
get_task( &self, task_id: u32, #[cfg(feature = "oh")] system: SystemConfig, client_manager: &ClientManagerEntry, upload_resume: bool, ) -> Result<Arc<RequestTask>, ErrorCode>553     pub(crate) fn get_task(
554         &self,
555         task_id: u32,
556         #[cfg(feature = "oh")] system: SystemConfig,
557         client_manager: &ClientManagerEntry,
558         upload_resume: bool,
559     ) -> Result<Arc<RequestTask>, ErrorCode> {
560         // If this task exists in `user_file_map`,get it from this map.
561         if let Some(task) = self.user_file_tasks.lock().unwrap().get(&task_id) {
562             return Ok(task.clone());
563         }
564 
565         // 此处需要根据 task_id 从数据库构造指定的任务。
566         let config = match self.get_task_config(task_id) {
567             Some(config) => config,
568             None => return Err(ErrorCode::TaskNotFound),
569         };
570         let task_id = config.common_data.task_id;
571 
572         let task_info = match self.get_task_info(task_id) {
573             Some(info) => info,
574             None => return Err(ErrorCode::TaskNotFound),
575         };
576 
577         let state = State::from(task_info.progress.common_data.state);
578         debug!("get_task {} state is {:?}", task_id, state);
579         if state == State::Removed {
580             error!("get_task state is Removed, {}", task_id);
581             return Err(ErrorCode::TaskStateErr);
582         }
583 
584         match RequestTask::new_by_info(
585             config,
586             #[cfg(feature = "oh")]
587             system,
588             task_info,
589             client_manager.clone(),
590             upload_resume,
591         ) {
592             Ok(task) => Ok(Arc::new(task)),
593             Err(e) => {
594                 error!("new RequestTask failed {}, err: {:?}", task_id, e);
595                 Err(e)
596             }
597         }
598     }
599 }
600 
601 unsafe impl Send for RequestDb {}
602 unsafe impl Sync for RequestDb {}
603 
604 #[cfg(feature = "oh")]
605 
606 extern "C" {
DeleteCTaskConfig(ptr: *const CTaskConfig)607     fn DeleteCTaskConfig(ptr: *const CTaskConfig);
DeleteCTaskInfo(ptr: *const CTaskInfo)608     fn DeleteCTaskInfo(ptr: *const CTaskInfo);
GetTaskInfo(task_id: u32) -> *const CTaskInfo609     fn GetTaskInfo(task_id: u32) -> *const CTaskInfo;
QueryTaskConfig(task_id: u32) -> *const CTaskConfig610     fn QueryTaskConfig(task_id: u32) -> *const CTaskConfig;
RecordRequestTask(info: *const CTaskInfo, config: *const CTaskConfig) -> bool611     fn RecordRequestTask(info: *const CTaskInfo, config: *const CTaskConfig) -> bool;
UpdateRequestTask(id: u32, info: *const CUpdateInfo) -> bool612     fn UpdateRequestTask(id: u32, info: *const CUpdateInfo) -> bool;
613 }
614 
615 #[cxx::bridge(namespace = "OHOS::Request")]
616 mod ffi {
617     #[derive(Clone, Debug, Copy)]
618     pub(crate) struct TaskQosInfo {
619         pub(crate) task_id: u32,
620         pub(crate) action: u8,
621         pub(crate) mode: u8,
622         pub(crate) state: u8,
623         pub(crate) priority: u32,
624     }
625 
626     unsafe extern "C++" {
627         include!("c_request_database.h");
628         type RequestDataBase;
GetDatabaseInstance(path: &str, encrypt: bool) -> *mut RequestDataBase629         fn GetDatabaseInstance(path: &str, encrypt: bool) -> *mut RequestDataBase;
ExecuteSql(self: Pin<&mut RequestDataBase>, sql: &str) -> i32630         fn ExecuteSql(self: Pin<&mut RequestDataBase>, sql: &str) -> i32;
QueryInteger(self: Pin<&mut RequestDataBase>, sql: &str, v: &mut Vec<i64>) -> i32631         fn QueryInteger(self: Pin<&mut RequestDataBase>, sql: &str, v: &mut Vec<i64>) -> i32;
GetAppTaskQosInfos( self: Pin<&mut RequestDataBase>, sql: &str, v: &mut Vec<TaskQosInfo>, ) -> i32632         fn GetAppTaskQosInfos(
633             self: Pin<&mut RequestDataBase>,
634             sql: &str,
635             v: &mut Vec<TaskQosInfo>,
636         ) -> i32;
GetTaskQosInfo(self: Pin<&mut RequestDataBase>, sql: &str, res: &mut TaskQosInfo) -> i32637         fn GetTaskQosInfo(self: Pin<&mut RequestDataBase>, sql: &str, res: &mut TaskQosInfo)
638             -> i32;
639     }
640 }
641 
642 #[cfg(feature = "oh")]
643 #[cfg(test)]
644 mod test {
645     use super::RequestDb;
646     use crate::config::{Action, Mode};
647     use crate::task::info::State;
648     use crate::tests::{lock_database, test_init};
649     use crate::utils::get_current_timestamp;
650     use crate::utils::task_id_generator::TaskIdGenerator;
651 
652     #[test]
ut_database_base()653     fn ut_database_base() {
654         test_init();
655         let _lock = lock_database();
656 
657         let task_id = TaskIdGenerator::generate();
658         let db = RequestDb::get_instance();
659         db.execute(&format!(
660             "INSERT INTO request_task (task_id, bundle) VALUES ({}, 'example_bundle')",
661             task_id
662         ))
663         .unwrap();
664 
665         let tasks =
666             db.query_integer("SELECT task_id FROM request_task WHERE bundle = 'example_bundle'");
667         assert!(tasks.contains(&task_id));
668     }
669 
670     #[test]
ut_database_contains_task()671     fn ut_database_contains_task() {
672         test_init();
673         let _lock = lock_database();
674         let task_id = TaskIdGenerator::generate();
675         let db = RequestDb::get_instance();
676         db.execute(&format!(
677             "INSERT INTO request_task (task_id, bundle) VALUES ({}, 'example_bundle')",
678             task_id
679         ))
680         .unwrap();
681 
682         assert!(db.contains_task(task_id));
683     }
684 
685     #[test]
ut_database_query_task_token_id()686     fn ut_database_query_task_token_id() {
687         test_init();
688         let _lock = lock_database();
689 
690         let task_id = TaskIdGenerator::generate();
691         let token_id = 123456789;
692         let db = RequestDb::get_instance();
693         db.execute(&format!(
694             "INSERT INTO request_task (task_id, token_id) VALUES ({}, {})",
695             task_id, token_id
696         ))
697         .unwrap();
698 
699         assert_eq!(db.query_task_token_id(task_id).unwrap(), token_id);
700     }
701 
702     #[test]
ut_database_app_task_qos_info()703     fn ut_database_app_task_qos_info() {
704         test_init();
705         let _lock = lock_database();
706         let task_id = TaskIdGenerator::generate();
707         let db = RequestDb::get_instance();
708         let priority = get_current_timestamp() as u32;
709         db.execute(&format!(
710             "INSERT INTO request_task (task_id, action, mode, state, priority) VALUES ({}, {}, {}, {}, {})",
711             task_id,
712             Action::Download.repr,
713             Mode::FrontEnd.repr,
714             State::Completed.repr,
715             priority,
716         ))
717         .unwrap();
718 
719         let info = db.get_task_qos_info(task_id).unwrap();
720         assert_eq!(info.task_id, task_id);
721         assert_eq!(info.action, Action::Download.repr);
722         assert_eq!(info.mode, Mode::FrontEnd.repr);
723         assert_eq!(info.state, State::Completed.repr);
724         assert_eq!(info.priority, priority);
725     }
726 }
727