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