/* * Copyright (C) 2023 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use hdc::common::base::Base; use hdc::common::filemanager::FileManager; use hdc::common::hdcfile; use hdc::common::hdctransfer::{self, HdcTransferBase}; use hdc::config::HdcCommand; use hdc::config::TaskMessage; use hdc::config::TRANSFER_FUNC_NAME; use hdc::config::{self, INSTALL_TAR_MAX_CNT}; use hdc::serializer::serialize::Serialization; use hdc::transfer; use hdc::transfer::EchoLevel; use hdc::utils; use std::collections::HashMap; use std::path::PathBuf; use std::sync::Arc; use ylong_runtime::sync::Mutex; #[cfg(feature = "host")] extern crate ylong_runtime_static as ylong_runtime; use hdc::tar::compress::Compress; pub struct HostAppTask { pub transfer: HdcTransferBase, pub printed_msg_len: usize, } impl HostAppTask { /// complie failed ,associated function `new` is never used pub fn new(_session_id: u32, _channel_id: u32) -> Self { Self { transfer: HdcTransferBase::new(_session_id, _channel_id), printed_msg_len: 0, } } } type HostAppTask_ = Arc>; type HostAppTaskMap_ = Arc>>; pub struct HostAppTaskMap {} impl HostAppTaskMap { fn get_instance() -> HostAppTaskMap_ { static mut HOSTAPPTASKMAP: Option = None; unsafe { HOSTAPPTASKMAP .get_or_insert_with(|| Arc::new(Mutex::new(HashMap::new()))) .clone() } } pub async fn put(session_id: u32, channel_id: u32, host_app_task: HostAppTask) { let arc_map = Self::get_instance(); let mut map = arc_map.lock().await; map.insert( (session_id, channel_id), Arc::new(Mutex::new(host_app_task)), ); } pub async fn exist(session_id: u32, channel_id: u32) -> Result { let arc_map = Self::get_instance(); let map = arc_map.lock().await; Ok(map.contains_key(&(session_id, channel_id))) } pub async fn remove(session_id: u32, channel_id: u32) -> Option { let arc_map = Self::get_instance(); let mut map = arc_map.lock().await; map.remove(&(session_id, channel_id)) } pub async fn get(session_id: u32, channel_id: u32) -> Option { let arc_map = Self::get_instance(); let map = arc_map.lock().await; let Some(arc_task) = map.get(&(session_id, channel_id)) else { return None; }; Some(arc_task.clone()) } } async fn check_install_continue( session_id: u32, channel_id: u32, mode_type: config::AppModeType, str: String, ) -> bool { let mode_desc = match mode_type { config::AppModeType::Install => String::from("App install"), config::AppModeType::UnInstall => String::from("App uninstall"), }; let Some(arc_task) = HostAppTaskMap::get(session_id, channel_id).await else { hdc::error!("Get host app task failed"); return false; }; let mut task = arc_task.lock().await; let msg = str[task.printed_msg_len..].to_owned(); let local_path = if !task.transfer.local_tar_raw_path.is_empty() { &task.transfer.local_tar_raw_path } else { &task.transfer.local_path }; let message = format!("{} path:{}, queuesize:{}, msg:{}", mode_desc, local_path, task.transfer.task_queue.len(), msg); hdc::info!("{message}"); task.printed_msg_len = str.len(); let _ = transfer::send_channel_msg(channel_id, EchoLevel::INFO, message).await; if task.transfer.task_queue.is_empty() { let _ = transfer::send_channel_msg(channel_id, EchoLevel::INFO, String::from("AppMod finish")).await; task_finish(session_id, channel_id).await; hdctransfer::close_channel(channel_id).await; return false; } drop(task); if let Err(err_msg) = install_single(session_id, channel_id).await { let _ = transfer::send_channel_msg(channel_id, EchoLevel::FAIL, err_msg).await; task_finish(session_id, channel_id).await; return false; } put_app_check(session_id, channel_id).await; true } async fn do_app_uninstall(session_id: u32, channel_id: u32, payload: Vec) { hdc::info!("send HdcCommand::AppUninstall"); let app_uninstall_message = TaskMessage { channel_id, command: HdcCommand::AppUninstall, payload }; transfer::put(session_id, app_uninstall_message).await; } async fn do_app_finish(session_id: u32, channel_id: u32, payload: &[u8]) -> bool { let mode = config::AppModeType::try_from(payload[0]); if let Ok(mode_type) = mode { let str = match String::from_utf8(payload[2..].to_vec()) { Ok(str) => str, Err(err) => { hdc::error!("do_app_finish from_utf8 error, {err}"); return false; } }; return check_install_continue(session_id, channel_id, mode_type, str).await; } false } fn dir_to_tar(dir_path: PathBuf) -> Result { let mut compress = Compress::new(); compress.updata_prefix(dir_path.clone()); if let Err(err) = compress.add_path(&dir_path) { return Err(format!("Package as tar and add path error, {err}")); } compress.updata_max_count(INSTALL_TAR_MAX_CNT); let tar_name = utils::get_pseudo_random_u32().to_string() + ".tar"; let tar_path = std::env::temp_dir().join(tar_name); match compress.compress(tar_path.clone()) { Ok(_) => Ok(tar_path.display().to_string()), Err(err) => Err(format!("compress {} fial, {}", tar_path.display(), err)), } } async fn task_finish(session_id: u32, channel_id: u32) { HostAppTaskMap::remove(session_id, channel_id).await; hdctransfer::transfer_task_finish(channel_id, session_id).await } async fn put_app_check(session_id: u32, channel_id: u32) { let Some(arc_task) = HostAppTaskMap::get(session_id, channel_id).await else { hdc::error!("Get host app task failed"); return; }; let task = arc_task.lock().await; hdc::info!("send HdcCommand::AppCheck"); let file_check_message = TaskMessage { channel_id, command: HdcCommand::AppCheck, payload: task.transfer.transfer_config.serialize(), }; transfer::put(session_id, file_check_message).await } async fn install_single(session_id: u32, channel_id: u32) -> Result<(), String> { let Some(arc_task) = HostAppTaskMap::get(session_id, channel_id).await else { hdc::error!("Get host app task failed"); return Err("Internal error, Pls try again".to_owned()); }; let mut task = arc_task.lock().await; match task.transfer.task_queue.pop() { Some(loc_path) => { let loc_pathbuff = PathBuf::from(loc_path.clone()); if loc_pathbuff.is_file() { task.transfer.local_path = loc_path; task.transfer.local_tar_raw_path = String::new(); } else if loc_pathbuff.is_dir() { match dir_to_tar(loc_pathbuff) { Ok(tar_file) => { hdc::info!("dir_to_tar success, path = {}", tar_file); task.transfer.local_path = tar_file; task.transfer.local_tar_raw_path = loc_path; } Err(err) => { hdc::error!("{}", err); return Err("Folder packaging failed".to_owned()); } } } else { return Err(format!("Error opening file: no such file or directory, path:{loc_path}")); } } None => { hdc::info!("task_queue is empty, not need install"); return Err("Not any installation package was found".to_owned()); } } let local_path = task.transfer.local_path.clone(); let mut file_manager = FileManager::new(local_path.clone()); let (open_result, error_msg) = file_manager.open(); if open_result { let file_size = file_manager.file_size(); task.transfer.transfer_config.file_size = file_size; task.transfer.file_size = file_size; task.transfer.transfer_config.optional_name = utils::get_pseudo_random_u32().to_string(); if let Some(index) = local_path.rfind('.') { let str = local_path.as_str(); task.transfer .transfer_config .optional_name .push_str(&str[index..]); } task.transfer.transfer_config.path = task.transfer.remote_path.clone(); Ok(()) } else { hdc::error!("file_manager.open {error_msg}"); Err(error_msg) } } async fn init_install(session_id: u32, channel_id: u32, command: &String) -> Result<(), String> { let (argv, argc) = Base::split_command_to_args(command); if argc < 1 { hdc::error!("argc {argc}, {command}"); return Err("Invalid parameter".to_owned()); } let Some(arc_task) = HostAppTaskMap::get(session_id, channel_id).await else { hdc::error!("Get host app task failed"); return Err("Internal error, Pls try again".to_owned()); }; let mut task = arc_task.lock().await; let mut i = 1usize; let mut options = String::from(""); while i < argc as usize { if argv[i] == "-cwd" { if i + 1 < argc as usize { task.transfer.transfer_config.client_cwd = argv[i + 1].clone(); i += 1; } } else if argv[i].starts_with('-') { if !options.is_empty() { options.push(' '); } options.push_str(&argv[i].clone()); } else { let mut path = argv[i].clone() as String; path = Base::extract_relative_path( &task.transfer.transfer_config.client_cwd, path.as_str(), ); if path.ends_with(".hap") || path.ends_with(".hsp") { task.transfer.task_queue.push(path); } else { let pathbuff = PathBuf::from(path.clone()); if pathbuff.is_dir() { task.transfer.task_queue.push(path); } } } i += 1; } if task.transfer.task_queue.is_empty() { return Err("Not any installation package was found".to_owned()); } task.transfer.transfer_config.options = options.clone(); task.transfer.transfer_config.function_name = TRANSFER_FUNC_NAME.to_string(); task.transfer.is_master = true; drop(task); install_single(session_id, channel_id).await } async fn task_app_install(session_id: u32, channel_id: u32, payload: &[u8]) -> Result<(), String> { match String::from_utf8(payload.to_vec()) { Ok(str) => { hdc::info!("cmd : {str}"); init_install(session_id, channel_id, &str).await?; hdcfile::wake_up_slaver(session_id, channel_id).await; put_app_check(session_id, channel_id).await } Err(e) => { hdc::error!("error {}", e); let err_msg = "Internal error, Pls try again".to_owned(); return Err(err_msg); } } Ok(()) } async fn task_app_uninstall(session_id: u32, channel_id: u32, payload: &[u8]) -> Result<(), String> { match String::from_utf8(payload.to_vec()) { Ok(str) => { hdc::info!("cmd {str}"); let (argv, argc) = Base::split_command_to_args(&str); if argc < 1 { hdc::error!("argc {argc}"); let err_msg = String::from("Invalid input parameters"); return Err(err_msg); } let (_opt, pack): (Vec<&String>, Vec<&String>) = argv.iter().partition(|arg| arg.starts_with('-')); if pack.len() <= 1 { let err_msg = String::from("Invalid input parameters"); return Err(err_msg); } let options = argv[1..].join(" "); let payload = options.as_bytes().to_vec(); do_app_uninstall(session_id, channel_id, payload).await; } Err(e) => { println!("error {}", e); let err_msg = "Internal error, Pls try again".to_owned(); return Err(err_msg); } } Ok(()) } pub async fn command_dispatch( session_id: u32, channel_id: u32, command: HdcCommand, payload: &[u8], ) -> Result { match command { HdcCommand::AppInit => { if let Err(err_msg) = task_app_install(session_id, channel_id, payload).await { let _ = transfer::send_channel_msg(channel_id, EchoLevel::FAIL, err_msg).await; transfer::TcpMap::end(channel_id).await; task_finish(session_id, channel_id).await; return Ok(false); } } HdcCommand::AppBegin => { let Some(arc_task) = HostAppTaskMap::get(session_id, channel_id).await else { hdc::error!("Get host app task failed"); let err_msg = "Internal error, Pls try again".to_owned(); let _ = transfer::send_channel_msg(channel_id, EchoLevel::FAIL, err_msg).await; transfer::TcpMap::end(channel_id).await; task_finish(session_id, channel_id).await; return Ok(false); }; let task = arc_task.lock().await; hdc::info!("recv HdcCommand::AppBegin"); hdctransfer::transfer_begin(&task.transfer, HdcCommand::AppData).await; } HdcCommand::AppUninstall => { if let Err(err_msg) = task_app_uninstall(session_id, channel_id, payload).await { let _ = transfer::send_channel_msg(channel_id, EchoLevel::FAIL, err_msg).await; transfer::TcpMap::end(channel_id).await; task_finish(session_id, channel_id).await; return Ok(false); } }, HdcCommand::AppFinish => { hdc::info!("recv HdcCommand::AppFinish"); do_app_finish(session_id, channel_id, payload).await; } _ => { println!("other command"); } } Ok(true) }