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