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::fs::File; 16 use std::os::fd::FromRawFd; 17 18 pub use ffi::{Action, Mode}; 19 20 cfg_oh! { 21 use ipc::parcel::Serialize; 22 } 23 24 use super::reason::Reason; 25 use crate::manage::network::{NetworkState, NetworkType}; 26 use crate::utils::c_wrapper::{CFileSpec, CFormItem, CStringWrapper}; 27 use crate::utils::form_item::{FileSpec, FormItem}; 28 use crate::utils::hashmap_to_string; 29 #[cxx::bridge(namespace = "OHOS::Request")] 30 mod ffi { 31 /// Action 32 #[derive(Clone, Copy, PartialEq, Debug)] 33 #[repr(u8)] 34 pub enum Action { 35 /// Download 36 Download = 0, 37 /// Upload 38 Upload, 39 /// Any 40 Any, 41 } 42 43 /// Mode 44 #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] 45 #[repr(u8)] 46 pub enum Mode { 47 /// BackGround 48 BackGround = 0, 49 /// ForeGround 50 FrontEnd, 51 /// Any 52 Any, 53 } 54 } 55 56 #[derive(Clone, Copy, PartialEq, Debug)] 57 #[repr(u8)] 58 pub(crate) enum Version { 59 API9 = 1, 60 API10, 61 } 62 63 /// NetworkConfig 64 #[derive(Clone, Copy, PartialEq, Debug)] 65 #[repr(u8)] 66 pub enum NetworkConfig { 67 /// Any 68 Any = 0, 69 /// Wifi 70 Wifi, 71 /// Cellular 72 Cellular, 73 } 74 75 #[repr(C)] 76 #[derive(Copy, Clone, Debug)] 77 pub(crate) struct CommonTaskConfig { 78 pub(crate) task_id: u32, 79 pub(crate) uid: u64, 80 pub(crate) token_id: u64, 81 pub(crate) action: Action, 82 pub(crate) mode: Mode, 83 pub(crate) cover: bool, 84 pub(crate) network_config: NetworkConfig, 85 pub(crate) metered: bool, 86 pub(crate) roaming: bool, 87 pub(crate) retry: bool, 88 pub(crate) redirect: bool, 89 pub(crate) index: u32, 90 pub(crate) begins: u64, 91 pub(crate) ends: i64, 92 pub(crate) gauge: bool, 93 pub(crate) precise: bool, 94 pub(crate) priority: u32, 95 pub(crate) background: bool, 96 } 97 98 /// task config 99 #[derive(Clone, Debug)] 100 pub struct TaskConfig { 101 pub(crate) bundle: String, 102 pub(crate) bundle_type: u32, 103 pub(crate) atomic_account: String, 104 pub(crate) url: String, 105 pub(crate) title: String, 106 pub(crate) description: String, 107 pub(crate) method: String, 108 pub(crate) headers: HashMap<String, String>, 109 pub(crate) data: String, 110 pub(crate) token: String, 111 pub(crate) proxy: String, 112 pub(crate) certificate_pins: String, 113 pub(crate) extras: HashMap<String, String>, 114 pub(crate) version: Version, 115 pub(crate) form_items: Vec<FormItem>, 116 pub(crate) file_specs: Vec<FileSpec>, 117 pub(crate) body_file_paths: Vec<String>, 118 pub(crate) certs_path: Vec<String>, 119 pub(crate) common_data: CommonTaskConfig, 120 } 121 122 impl TaskConfig { satisfy_network(&self, network: &NetworkState) -> Result<(), Reason>123 pub(crate) fn satisfy_network(&self, network: &NetworkState) -> Result<(), Reason> { 124 // NetworkConfig::Cellular with NetworkType::Wifi is allowed 125 match network { 126 NetworkState::Offline => Err(Reason::NetworkOffline), 127 NetworkState::Online(info) => match self.common_data.network_config { 128 NetworkConfig::Any => Ok(()), 129 NetworkConfig::Wifi if info.network_type == NetworkType::Cellular => { 130 Err(Reason::UnsupportedNetworkType) 131 } 132 _ => { 133 if (self.common_data.roaming || !info.is_roaming) 134 && (self.common_data.metered || !info.is_metered) 135 { 136 Ok(()) 137 } else { 138 Err(Reason::UnsupportedNetworkType) 139 } 140 } 141 }, 142 } 143 } 144 satisfy_foreground(&self, top_uid: Option<u64>) -> bool145 pub(crate) fn satisfy_foreground(&self, top_uid: Option<u64>) -> bool { 146 self.common_data.mode == Mode::BackGround || Some(self.common_data.uid) == top_uid 147 } 148 } 149 150 pub(crate) struct ConfigSet { 151 pub(crate) headers: String, 152 pub(crate) extras: String, 153 pub(crate) form_items: Vec<CFormItem>, 154 pub(crate) file_specs: Vec<CFileSpec>, 155 pub(crate) body_file_names: Vec<CStringWrapper>, 156 pub(crate) certs_path: Vec<CStringWrapper>, 157 } 158 159 impl PartialOrd for Mode { partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering>160 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { 161 Some(self.cmp(other)) 162 } 163 } 164 165 impl Ord for Mode { cmp(&self, other: &Self) -> std::cmp::Ordering166 fn cmp(&self, other: &Self) -> std::cmp::Ordering { 167 let me = match *self { 168 Mode::FrontEnd => 0, 169 Mode::Any => 1, 170 Mode::BackGround => 2, 171 _ => unreachable!(), 172 }; 173 let other = match *other { 174 Mode::FrontEnd => 0, 175 Mode::Any => 1, 176 Mode::BackGround => 2, 177 _ => unreachable!(), 178 }; 179 me.cmp(&other) 180 } 181 } 182 183 impl From<u8> for Mode { from(value: u8) -> Self184 fn from(value: u8) -> Self { 185 match value { 186 0 => Mode::BackGround, 187 1 => Mode::FrontEnd, 188 _ => Mode::Any, 189 } 190 } 191 } 192 193 impl From<u8> for Action { from(value: u8) -> Self194 fn from(value: u8) -> Self { 195 match value { 196 0 => Action::Download, 197 1 => Action::Upload, 198 _ => Action::Any, 199 } 200 } 201 } 202 203 impl From<u8> for Version { from(value: u8) -> Self204 fn from(value: u8) -> Self { 205 match value { 206 2 => Version::API10, 207 _ => Version::API9, 208 } 209 } 210 } 211 212 impl From<u8> for NetworkConfig { from(value: u8) -> Self213 fn from(value: u8) -> Self { 214 match value { 215 0 => NetworkConfig::Any, 216 2 => NetworkConfig::Cellular, 217 _ => NetworkConfig::Wifi, 218 } 219 } 220 } 221 222 impl TaskConfig { build_config_set(&self) -> ConfigSet223 pub(crate) fn build_config_set(&self) -> ConfigSet { 224 ConfigSet { 225 headers: hashmap_to_string(&self.headers), 226 extras: hashmap_to_string(&self.extras), 227 form_items: self.form_items.iter().map(|x| x.to_c_struct()).collect(), 228 file_specs: self.file_specs.iter().map(|x| x.to_c_struct()).collect(), 229 body_file_names: self 230 .body_file_paths 231 .iter() 232 .map(CStringWrapper::from) 233 .collect(), 234 certs_path: self.certs_path.iter().map(CStringWrapper::from).collect(), 235 } 236 } 237 contains_user_file(&self) -> bool238 pub(crate) fn contains_user_file(&self) -> bool { 239 for specs in self.file_specs.iter() { 240 if specs.is_user_file { 241 return true; 242 } 243 } 244 false 245 } 246 } 247 248 impl Default for TaskConfig { default() -> Self249 fn default() -> Self { 250 Self { 251 bundle_type: 0, 252 atomic_account: "ohosAnonymousUid".to_string(), 253 bundle: "xxx".to_string(), 254 url: "".to_string(), 255 title: "xxx".to_string(), 256 description: "xxx".to_string(), 257 method: "GET".to_string(), 258 headers: Default::default(), 259 data: "".to_string(), 260 token: "xxx".to_string(), 261 proxy: "".to_string(), 262 extras: Default::default(), 263 version: Version::API10, 264 form_items: vec![], 265 file_specs: vec![], 266 body_file_paths: vec![], 267 certs_path: vec![], 268 certificate_pins: "".to_string(), 269 common_data: CommonTaskConfig { 270 task_id: 0, 271 uid: 0, 272 token_id: 0, 273 action: Action::Download, 274 mode: Mode::BackGround, 275 cover: false, 276 network_config: NetworkConfig::Any, 277 metered: false, 278 roaming: false, 279 retry: false, 280 redirect: true, 281 index: 0, 282 begins: 0, 283 ends: -1, 284 gauge: false, 285 precise: false, 286 priority: 0, 287 background: false, 288 }, 289 } 290 } 291 } 292 293 /// ConfigBuilder 294 pub struct ConfigBuilder { 295 inner: TaskConfig, 296 } 297 298 impl ConfigBuilder { 299 /// Create a new ConfigBuilder new() -> Self300 pub fn new() -> Self { 301 Self { 302 inner: TaskConfig::default(), 303 } 304 } 305 /// Set url url(&mut self, url: &str) -> &mut Self306 pub fn url(&mut self, url: &str) -> &mut Self { 307 self.inner.url = url.to_string(); 308 self 309 } 310 311 /// set version version(&mut self, version: u8) -> &mut Self312 pub fn version(&mut self, version: u8) -> &mut Self { 313 self.inner.version = version.into(); 314 self 315 } 316 317 /// Set title file_spec(&mut self, file: File) -> &mut Self318 pub fn file_spec(&mut self, file: File) -> &mut Self { 319 self.inner.file_specs.push(FileSpec::user_file(file)); 320 self 321 } 322 /// Set action action(&mut self, action: Action) -> &mut Self323 pub fn action(&mut self, action: Action) -> &mut Self { 324 self.inner.common_data.action = action; 325 self 326 } 327 328 /// Set mode mode(&mut self, mode: Mode) -> &mut Self329 pub fn mode(&mut self, mode: Mode) -> &mut Self { 330 self.inner.common_data.mode = mode; 331 self 332 } 333 334 /// Set bundle name bundle_name(&mut self, bundle_name: &str) -> &mut Self335 pub fn bundle_name(&mut self, bundle_name: &str) -> &mut Self { 336 self.inner.bundle = bundle_name.to_string(); 337 self 338 } 339 340 /// Set uid uid(&mut self, uid: u64) -> &mut Self341 pub fn uid(&mut self, uid: u64) -> &mut Self { 342 self.inner.common_data.uid = uid; 343 self 344 } 345 346 /// set network network(&mut self, network: NetworkConfig) -> &mut Self347 pub fn network(&mut self, network: NetworkConfig) -> &mut Self { 348 self.inner.common_data.network_config = network; 349 self 350 } 351 352 /// Set metered roaming(&mut self, roaming: bool) -> &mut Self353 pub fn roaming(&mut self, roaming: bool) -> &mut Self { 354 self.inner.common_data.roaming = roaming; 355 self 356 } 357 358 /// set metered metered(&mut self, metered: bool) -> &mut Self359 pub fn metered(&mut self, metered: bool) -> &mut Self { 360 self.inner.common_data.metered = metered; 361 self 362 } 363 364 /// build build(&mut self) -> TaskConfig365 pub fn build(&mut self) -> TaskConfig { 366 self.inner.clone() 367 } 368 369 /// redirect redirect(&mut self, redirect: bool) -> &mut Self370 pub fn redirect(&mut self, redirect: bool) -> &mut Self { 371 self.inner.common_data.redirect = redirect; 372 self 373 } 374 375 /// begins begins(&mut self, begins: u64) -> &mut Self376 pub fn begins(&mut self, begins: u64) -> &mut Self { 377 self.inner.common_data.begins = begins; 378 self 379 } 380 381 /// ends ends(&mut self, ends: u64) -> &mut Self382 pub fn ends(&mut self, ends: u64) -> &mut Self { 383 self.inner.common_data.ends = ends as i64; 384 self 385 } 386 387 /// method method(&mut self, metered: &str) -> &mut Self388 pub fn method(&mut self, metered: &str) -> &mut Self { 389 self.inner.method = metered.to_string(); 390 self 391 } 392 393 /// retry retry(&mut self, retry: bool) -> &mut Self394 pub fn retry(&mut self, retry: bool) -> &mut Self { 395 self.inner.common_data.retry = retry; 396 self 397 } 398 } 399 400 #[cfg(feature = "oh")] 401 impl Serialize for TaskConfig { serialize(&self, parcel: &mut ipc::parcel::MsgParcel) -> ipc::IpcResult<()>402 fn serialize(&self, parcel: &mut ipc::parcel::MsgParcel) -> ipc::IpcResult<()> { 403 parcel.write(&(self.common_data.action.repr as u32))?; 404 parcel.write(&(self.version as u32))?; 405 parcel.write(&(self.common_data.mode.repr as u32))?; 406 parcel.write(&self.bundle_type)?; 407 parcel.write(&self.common_data.cover)?; 408 parcel.write(&(self.common_data.network_config as u32))?; 409 parcel.write(&(self.common_data.metered))?; 410 parcel.write(&self.common_data.roaming)?; 411 parcel.write(&(self.common_data.retry))?; 412 parcel.write(&(self.common_data.redirect))?; 413 parcel.write(&(self.common_data.background))?; 414 parcel.write(&self.common_data.index)?; 415 parcel.write(&(self.common_data.begins as i64))?; 416 parcel.write(&self.common_data.ends)?; 417 parcel.write(&self.common_data.gauge)?; 418 parcel.write(&self.common_data.precise)?; 419 parcel.write(&self.common_data.priority)?; 420 parcel.write(&self.url)?; 421 parcel.write(&self.title)?; 422 parcel.write(&self.method)?; 423 parcel.write(&self.token)?; 424 parcel.write(&self.description)?; 425 parcel.write(&self.data)?; 426 parcel.write(&self.proxy)?; 427 parcel.write(&self.certificate_pins)?; 428 429 parcel.write(&(self.certs_path.len() as u32))?; 430 for cert_path in &self.certs_path { 431 parcel.write(cert_path)?; 432 } 433 434 parcel.write(&(self.form_items.len() as u32))?; 435 for form_item in &self.form_items { 436 parcel.write(&form_item.name)?; 437 parcel.write(&form_item.value)?; 438 } 439 parcel.write(&(self.file_specs.len() as u32))?; 440 for file_spec in &self.file_specs { 441 parcel.write(&file_spec.name)?; 442 parcel.write(&file_spec.path)?; 443 parcel.write(&file_spec.file_name)?; 444 parcel.write(&file_spec.mime_type)?; 445 parcel.write(&file_spec.is_user_file)?; 446 if file_spec.is_user_file { 447 let file = unsafe { File::from_raw_fd(file_spec.fd.unwrap()) }; 448 parcel.write_file(file)?; 449 } 450 } 451 452 parcel.write(&(self.body_file_paths.len() as u32))?; 453 for body_file_paths in self.body_file_paths.iter() { 454 parcel.write(body_file_paths)?; 455 } 456 parcel.write(&(self.headers.len() as u32))?; 457 for header in self.headers.iter() { 458 parcel.write(header.0)?; 459 parcel.write(header.1)?; 460 } 461 462 parcel.write(&(self.extras.len() as u32))?; 463 for extra in self.extras.iter() { 464 parcel.write(extra.0)?; 465 parcel.write(extra.1)?; 466 } 467 468 Ok(()) 469 } 470 } 471 472 #[cfg(test)] 473 mod test { 474 use super::*; 475 #[test] ut_enum_action()476 fn ut_enum_action() { 477 assert_eq!(Action::Download.repr, 0); 478 assert_eq!(Action::Upload.repr, 1); 479 assert_eq!(Action::Any.repr, 2); 480 } 481 482 #[test] ut_enum_mode()483 fn ut_enum_mode() { 484 assert_eq!(Mode::BackGround.repr, 0); 485 assert_eq!(Mode::FrontEnd.repr, 1); 486 assert_eq!(Mode::Any.repr, 2); 487 } 488 489 #[test] ut_enum_version()490 fn ut_enum_version() { 491 assert_eq!(Version::API9 as u32, 1); 492 assert_eq!(Version::API10 as u32, 2); 493 } 494 495 #[test] ut_enum_network_config()496 fn ut_enum_network_config() { 497 assert_eq!(NetworkConfig::Any as u32, 0); 498 assert_eq!(NetworkConfig::Wifi as u32, 1); 499 assert_eq!(NetworkConfig::Cellular as u32, 2); 500 } 501 } 502