* 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 {
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<Mutex<HostAppTask>>;
type HostAppTaskMap_ = Arc<Mutex<HashMap<(u32, u32), HostAppTask_>>>;
pub struct HostAppTaskMap {}
impl HostAppTaskMap {
fn get_instance() -> HostAppTaskMap_ {
static mut HOSTAPPTASKMAP: Option<HostAppTaskMap_> = 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<bool, ()> {
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<HostAppTask_> {
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<HostAppTask_> {
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<u8>) {
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<String, String> {
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<bool, &str> {
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)
}