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, HashSet}; 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 pub(crate) multipart: bool, 97 } 98 99 /// task config 100 #[derive(Clone, Debug)] 101 pub struct TaskConfig { 102 pub(crate) bundle: String, 103 pub(crate) bundle_type: u32, 104 pub(crate) atomic_account: String, 105 pub(crate) url: String, 106 pub(crate) title: String, 107 pub(crate) description: String, 108 pub(crate) method: String, 109 pub(crate) headers: HashMap<String, String>, 110 pub(crate) data: String, 111 pub(crate) token: String, 112 pub(crate) proxy: String, 113 pub(crate) certificate_pins: String, 114 pub(crate) extras: HashMap<String, String>, 115 pub(crate) version: Version, 116 pub(crate) form_items: Vec<FormItem>, 117 pub(crate) file_specs: Vec<FileSpec>, 118 pub(crate) body_file_paths: Vec<String>, 119 pub(crate) certs_path: Vec<String>, 120 pub(crate) common_data: CommonTaskConfig, 121 } 122 123 impl TaskConfig { satisfy_network(&self, network: &NetworkState) -> Result<(), Reason>124 pub(crate) fn satisfy_network(&self, network: &NetworkState) -> Result<(), Reason> { 125 // NetworkConfig::Cellular with NetworkType::Wifi is allowed 126 match network { 127 NetworkState::Offline => Err(Reason::NetworkOffline), 128 NetworkState::Online(info) => match self.common_data.network_config { 129 NetworkConfig::Any => Ok(()), 130 NetworkConfig::Wifi if info.network_type == NetworkType::Cellular => { 131 Err(Reason::UnsupportedNetworkType) 132 } 133 _ => { 134 if (self.common_data.roaming || !info.is_roaming) 135 && (self.common_data.metered || !info.is_metered) 136 { 137 Ok(()) 138 } else { 139 Err(Reason::UnsupportedNetworkType) 140 } 141 } 142 }, 143 } 144 } 145 satisfy_foreground(&self, foreground_abilities: &HashSet<u64>) -> bool146 pub(crate) fn satisfy_foreground(&self, foreground_abilities: &HashSet<u64>) -> bool { 147 self.common_data.mode == Mode::BackGround 148 || foreground_abilities.contains(&self.common_data.uid) 149 } 150 } 151 152 pub(crate) struct ConfigSet { 153 pub(crate) headers: String, 154 pub(crate) extras: String, 155 pub(crate) form_items: Vec<CFormItem>, 156 pub(crate) file_specs: Vec<CFileSpec>, 157 pub(crate) body_file_names: Vec<CStringWrapper>, 158 pub(crate) certs_path: Vec<CStringWrapper>, 159 } 160 161 impl PartialOrd for Mode { partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering>162 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { 163 Some(self.cmp(other)) 164 } 165 } 166 167 impl Ord for Mode { cmp(&self, other: &Self) -> std::cmp::Ordering168 fn cmp(&self, other: &Self) -> std::cmp::Ordering { 169 let me = match *self { 170 Mode::FrontEnd => 0, 171 Mode::Any => 1, 172 Mode::BackGround => 2, 173 _ => unreachable!(), 174 }; 175 let other = match *other { 176 Mode::FrontEnd => 0, 177 Mode::Any => 1, 178 Mode::BackGround => 2, 179 _ => unreachable!(), 180 }; 181 me.cmp(&other) 182 } 183 } 184 185 impl From<u8> for Mode { from(value: u8) -> Self186 fn from(value: u8) -> Self { 187 match value { 188 0 => Mode::BackGround, 189 1 => Mode::FrontEnd, 190 _ => Mode::Any, 191 } 192 } 193 } 194 195 impl From<u8> for Action { from(value: u8) -> Self196 fn from(value: u8) -> Self { 197 match value { 198 0 => Action::Download, 199 1 => Action::Upload, 200 _ => Action::Any, 201 } 202 } 203 } 204 205 impl From<u8> for Version { from(value: u8) -> Self206 fn from(value: u8) -> Self { 207 match value { 208 2 => Version::API10, 209 _ => Version::API9, 210 } 211 } 212 } 213 214 impl From<u8> for NetworkConfig { from(value: u8) -> Self215 fn from(value: u8) -> Self { 216 match value { 217 0 => NetworkConfig::Any, 218 2 => NetworkConfig::Cellular, 219 _ => NetworkConfig::Wifi, 220 } 221 } 222 } 223 224 impl TaskConfig { build_config_set(&self) -> ConfigSet225 pub(crate) fn build_config_set(&self) -> ConfigSet { 226 ConfigSet { 227 headers: hashmap_to_string(&self.headers), 228 extras: hashmap_to_string(&self.extras), 229 form_items: self.form_items.iter().map(|x| x.to_c_struct()).collect(), 230 file_specs: self.file_specs.iter().map(|x| x.to_c_struct()).collect(), 231 body_file_names: self 232 .body_file_paths 233 .iter() 234 .map(CStringWrapper::from) 235 .collect(), 236 certs_path: self.certs_path.iter().map(CStringWrapper::from).collect(), 237 } 238 } 239 contains_user_file(&self) -> bool240 pub(crate) fn contains_user_file(&self) -> bool { 241 for specs in self.file_specs.iter() { 242 if specs.is_user_file { 243 return true; 244 } 245 } 246 false 247 } 248 } 249 250 impl Default for TaskConfig { default() -> Self251 fn default() -> Self { 252 Self { 253 bundle_type: 0, 254 atomic_account: "ohosAnonymousUid".to_string(), 255 bundle: "xxx".to_string(), 256 url: "".to_string(), 257 title: "xxx".to_string(), 258 description: "xxx".to_string(), 259 method: "GET".to_string(), 260 headers: Default::default(), 261 data: "".to_string(), 262 token: "xxx".to_string(), 263 proxy: "".to_string(), 264 extras: Default::default(), 265 version: Version::API10, 266 form_items: vec![], 267 file_specs: vec![], 268 body_file_paths: vec![], 269 certs_path: vec![], 270 certificate_pins: "".to_string(), 271 common_data: CommonTaskConfig { 272 task_id: 0, 273 uid: 0, 274 token_id: 0, 275 action: Action::Download, 276 mode: Mode::BackGround, 277 cover: false, 278 network_config: NetworkConfig::Any, 279 metered: false, 280 roaming: false, 281 retry: false, 282 redirect: true, 283 index: 0, 284 begins: 0, 285 ends: -1, 286 gauge: false, 287 precise: false, 288 priority: 0, 289 background: false, 290 multipart: false, 291 }, 292 } 293 } 294 } 295 296 /// ConfigBuilder 297 pub struct ConfigBuilder { 298 inner: TaskConfig, 299 } 300 301 impl ConfigBuilder { 302 /// Create a new ConfigBuilder new() -> Self303 pub fn new() -> Self { 304 Self { 305 inner: TaskConfig::default(), 306 } 307 } 308 /// Set url url(&mut self, url: &str) -> &mut Self309 pub fn url(&mut self, url: &str) -> &mut Self { 310 self.inner.url = url.to_string(); 311 self 312 } 313 314 /// set version version(&mut self, version: u8) -> &mut Self315 pub fn version(&mut self, version: u8) -> &mut Self { 316 self.inner.version = version.into(); 317 self 318 } 319 320 /// Set title file_spec(&mut self, file: File) -> &mut Self321 pub fn file_spec(&mut self, file: File) -> &mut Self { 322 self.inner.file_specs.push(FileSpec::user_file(file)); 323 self 324 } 325 /// Set action action(&mut self, action: Action) -> &mut Self326 pub fn action(&mut self, action: Action) -> &mut Self { 327 self.inner.common_data.action = action; 328 self 329 } 330 331 /// Set mode mode(&mut self, mode: Mode) -> &mut Self332 pub fn mode(&mut self, mode: Mode) -> &mut Self { 333 self.inner.common_data.mode = mode; 334 self 335 } 336 337 /// Set bundle name bundle_name(&mut self, bundle_name: &str) -> &mut Self338 pub fn bundle_name(&mut self, bundle_name: &str) -> &mut Self { 339 self.inner.bundle = bundle_name.to_string(); 340 self 341 } 342 343 /// Set uid uid(&mut self, uid: u64) -> &mut Self344 pub fn uid(&mut self, uid: u64) -> &mut Self { 345 self.inner.common_data.uid = uid; 346 self 347 } 348 349 /// set network network(&mut self, network: NetworkConfig) -> &mut Self350 pub fn network(&mut self, network: NetworkConfig) -> &mut Self { 351 self.inner.common_data.network_config = network; 352 self 353 } 354 355 /// Set metered roaming(&mut self, roaming: bool) -> &mut Self356 pub fn roaming(&mut self, roaming: bool) -> &mut Self { 357 self.inner.common_data.roaming = roaming; 358 self 359 } 360 361 /// set metered metered(&mut self, metered: bool) -> &mut Self362 pub fn metered(&mut self, metered: bool) -> &mut Self { 363 self.inner.common_data.metered = metered; 364 self 365 } 366 367 /// build build(&mut self) -> TaskConfig368 pub fn build(&mut self) -> TaskConfig { 369 self.inner.clone() 370 } 371 372 /// redirect redirect(&mut self, redirect: bool) -> &mut Self373 pub fn redirect(&mut self, redirect: bool) -> &mut Self { 374 self.inner.common_data.redirect = redirect; 375 self 376 } 377 378 /// begins begins(&mut self, begins: u64) -> &mut Self379 pub fn begins(&mut self, begins: u64) -> &mut Self { 380 self.inner.common_data.begins = begins; 381 self 382 } 383 384 /// ends ends(&mut self, ends: u64) -> &mut Self385 pub fn ends(&mut self, ends: u64) -> &mut Self { 386 self.inner.common_data.ends = ends as i64; 387 self 388 } 389 390 /// method method(&mut self, metered: &str) -> &mut Self391 pub fn method(&mut self, metered: &str) -> &mut Self { 392 self.inner.method = metered.to_string(); 393 self 394 } 395 396 /// retry retry(&mut self, retry: bool) -> &mut Self397 pub fn retry(&mut self, retry: bool) -> &mut Self { 398 self.inner.common_data.retry = retry; 399 self 400 } 401 } 402 403 #[cfg(feature = "oh")] 404 impl Serialize for TaskConfig { serialize(&self, parcel: &mut ipc::parcel::MsgParcel) -> ipc::IpcResult<()>405 fn serialize(&self, parcel: &mut ipc::parcel::MsgParcel) -> ipc::IpcResult<()> { 406 parcel.write(&(self.common_data.action.repr as u32))?; 407 parcel.write(&(self.version as u32))?; 408 parcel.write(&(self.common_data.mode.repr as u32))?; 409 parcel.write(&self.bundle_type)?; 410 parcel.write(&self.common_data.cover)?; 411 parcel.write(&(self.common_data.network_config as u32))?; 412 parcel.write(&(self.common_data.metered))?; 413 parcel.write(&self.common_data.roaming)?; 414 parcel.write(&(self.common_data.retry))?; 415 parcel.write(&(self.common_data.redirect))?; 416 parcel.write(&(self.common_data.background))?; 417 parcel.write(&(self.common_data.multipart))?; 418 parcel.write(&self.common_data.index)?; 419 parcel.write(&(self.common_data.begins as i64))?; 420 parcel.write(&self.common_data.ends)?; 421 parcel.write(&self.common_data.gauge)?; 422 parcel.write(&self.common_data.precise)?; 423 parcel.write(&self.common_data.priority)?; 424 parcel.write(&self.url)?; 425 parcel.write(&self.title)?; 426 parcel.write(&self.method)?; 427 parcel.write(&self.token)?; 428 parcel.write(&self.description)?; 429 parcel.write(&self.data)?; 430 parcel.write(&self.proxy)?; 431 parcel.write(&self.certificate_pins)?; 432 433 parcel.write(&(self.certs_path.len() as u32))?; 434 for cert_path in &self.certs_path { 435 parcel.write(cert_path)?; 436 } 437 438 parcel.write(&(self.form_items.len() as u32))?; 439 for form_item in &self.form_items { 440 parcel.write(&form_item.name)?; 441 parcel.write(&form_item.value)?; 442 } 443 parcel.write(&(self.file_specs.len() as u32))?; 444 for file_spec in &self.file_specs { 445 parcel.write(&file_spec.name)?; 446 parcel.write(&file_spec.path)?; 447 parcel.write(&file_spec.file_name)?; 448 parcel.write(&file_spec.mime_type)?; 449 parcel.write(&file_spec.is_user_file)?; 450 if file_spec.is_user_file { 451 let file = unsafe { File::from_raw_fd(file_spec.fd.unwrap()) }; 452 parcel.write_file(file)?; 453 } 454 } 455 456 parcel.write(&(self.body_file_paths.len() as u32))?; 457 for body_file_paths in self.body_file_paths.iter() { 458 parcel.write(body_file_paths)?; 459 } 460 parcel.write(&(self.headers.len() as u32))?; 461 for header in self.headers.iter() { 462 parcel.write(header.0)?; 463 parcel.write(header.1)?; 464 } 465 466 parcel.write(&(self.extras.len() as u32))?; 467 for extra in self.extras.iter() { 468 parcel.write(extra.0)?; 469 parcel.write(extra.1)?; 470 } 471 472 Ok(()) 473 } 474 } 475 476 #[cfg(test)] 477 mod test { 478 use super::*; 479 #[test] ut_enum_action()480 fn ut_enum_action() { 481 assert_eq!(Action::Download.repr, 0); 482 assert_eq!(Action::Upload.repr, 1); 483 assert_eq!(Action::Any.repr, 2); 484 } 485 486 #[test] ut_enum_mode()487 fn ut_enum_mode() { 488 assert_eq!(Mode::BackGround.repr, 0); 489 assert_eq!(Mode::FrontEnd.repr, 1); 490 assert_eq!(Mode::Any.repr, 2); 491 } 492 493 #[test] ut_enum_version()494 fn ut_enum_version() { 495 assert_eq!(Version::API9 as u32, 1); 496 assert_eq!(Version::API10 as u32, 2); 497 } 498 499 #[test] ut_enum_network_config()500 fn ut_enum_network_config() { 501 assert_eq!(NetworkConfig::Any as u32, 0); 502 assert_eq!(NetworkConfig::Wifi as u32, 1); 503 assert_eq!(NetworkConfig::Cellular as u32, 2); 504 } 505 } 506