c0bc2637创建于 5 天前历史提交
/*
 * Copyright (c) 2024-2025 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 std::{
    cell::RefCell,
    sync::{Arc, Mutex, OnceLock},
    os::raw::c_char, ffi::CString,
};
use ylong_runtime::task::JoinHandle;

use asset_common::{CallingInfo, Counter, Group, OwnerType, TaskManager, GROUP_SEPARATOR, SUCCESS};
use asset_crypto_manager::db_key_operator::get_db_key;
use asset_db_operator::{
    database::{Database, get_path},
    database_file_upgrade::{construct_splited_db_name, get_file_content},
    types::{column, QueryOptions},
};
use asset_file_operator::de_operator::create_user_de_dir;
use asset_log::{loge, logi, logw};
use asset_sdk::{macros_lib, AssetError, ErrCode, Extension, Result, Value,};
use asset_plugin_interface::plugin_interface::{ExtDbMap, IAssetPlugin, IAssetPluginCtx, RETURN_LIMIT, RETURN_OFFSET};

extern "C" {
    fn StoreKeyValue(user_id: i32, in_key: *const c_char, in_value: i32) -> bool;
    fn QueryValue(user_id: i32, in_key: *const c_char, out_value: *mut i32) -> i32;
    fn IsDeviceLocked(user_id: i32, is_device_locked: *mut bool) -> i32;
}

const DATASHARE_SUCCESS: i32 = 0;
const DATASHARE_FAIL: i32 = -1;

/// The asset_ext plugin.
#[derive(Default)]
pub struct AssetPlugin {
    lib: RefCell<Option<libloading::Library>>,
}

static ASSET_PLUGIN_LOCK: Mutex<()> = Mutex::new(());

unsafe impl Sync for AssetPlugin {}

impl AssetPlugin {
    fn new() -> Self {
        Self { lib: RefCell::new(None) }
    }

    /// Get the instance of AssetPlugin.
    pub fn get_instance() -> Arc<AssetPlugin> {
        static INSTANCE: OnceLock<Arc<AssetPlugin>> = OnceLock::new();
        INSTANCE.get_or_init(|| {
            logw!("Create instance for AssetPlugin.");
            Arc::new(AssetPlugin::new())
        }).clone()
    }

    /// Load the plugin.
    pub fn load_plugin(&self) -> Result<Box<dyn IAssetPlugin>> {
        unsafe {
            let _guard = ASSET_PLUGIN_LOCK.lock().unwrap();
            if self.lib.borrow().is_none() {
                logi!("start to load asset_ext plugin.");
                match libloading::Library::new("libasset_ext_ffi.z.so") {
                    Ok(lib) => *self.lib.borrow_mut() = Some(lib),
                    Err(err) => {
                        loge!("dlopen libasset_ext_ffi.z.so failed, err: {}", err);
                        return macros_lib::log_throw_error!(macros_lib::hisysevent::function!(),
                            ErrCode::InvalidArgument, "dlopen failed {}", err);
                    },
                };
            }

            let Some(ref lib) = *self.lib.borrow() else {
                return macros_lib::log_throw_error!(macros_lib::hisysevent::function!(),
                    ErrCode::InvalidArgument, "unexpected error");
            };

            let func = match lib
                .get::<libloading::Symbol<unsafe extern "C" fn() -> *mut dyn IAssetPlugin>>(b"_create_plugin")
            {
                Ok(func) => func,
                Err(err) => {
                    loge!("dlsym _create_plugin failed, err: {}", err);
                    return macros_lib::log_throw_error!(macros_lib::hisysevent::function!(),
                        ErrCode::InvalidArgument, "dlsym failed {}", err);
                },
            };

            let plugin_ptr = func();
            if plugin_ptr.is_null() {
                loge!("_create_plugin return null.");
                return macros_lib::log_throw_error!(macros_lib::hisysevent::function!(),
                    ErrCode::InvalidArgument, "_create_plugin return null.");
            }

            Ok(Box::from_raw(plugin_ptr))
        }
    }

    /// Unload plugin.
    pub fn unload_plugin(&self) {
        let _guard = ASSET_PLUGIN_LOCK.lock().unwrap();
        if self.lib.borrow().is_some() {
            *self.lib.borrow_mut() = None;
        }
    }
}

/// The asset_ext plugin context.
#[repr(C)]
pub struct AssetContext {
    /// The asset database's user id.
    pub user_id: i32,
}

fn get_db_name(user_id: i32, attributes: &ExtDbMap, is_ce: bool) -> std::result::Result<String, AssetError> {
    let owner_info = attributes.get_bytes_attr(&column::OWNER)?;
    let owner_type = attributes.get_enum_attr::<OwnerType>(&column::OWNER_TYPE)?;
    let calling_info = match attributes.get(&column::GROUP_ID) {
        Some(Value::Bytes(group)) => {
            let mut parts = group.split(|&byte| byte == GROUP_SEPARATOR as u8);
            let developer_id: Vec<u8> = parts.next().unwrap().to_vec();
            let group_id: Vec<u8> = parts.next().unwrap().to_vec();
            CallingInfo::new(user_id, owner_type, owner_info.to_vec(), Some(Group { developer_id, group_id }))
        },
        _ => CallingInfo::new(user_id, owner_type, owner_info.to_vec(), None),
    };
    construct_splited_db_name(&calling_info, is_ce)
}

fn get_query_options(attrs: &ExtDbMap) -> QueryOptions {
    QueryOptions {
        offset: match attrs.get(RETURN_OFFSET) {
            Some(Value::Number(offset)) => Some(*offset),
            _ => None,
        },
        limit: match attrs.get(RETURN_LIMIT) {
            Some(Value::Number(limit)) => Some(*limit),
            _ => None,
        },
        order_by: None,
        order: None,
        amend: None,
    }
}

fn get_db_key_and_build_db(user_id: i32, is_ce: bool, db_name: &str) -> std::result::Result<Database, u32> {
    let db_key = get_db_key(user_id, is_ce).map_err(|e| e.code as u32)?;
    Database::build_with_file_name(user_id, db_name, &db_key).map_err(|e| e.code as u32)
}

#[allow(dead_code)]
impl IAssetPluginCtx for AssetContext {
    /// Initializes the plugin before usage.
    fn init(&mut self, user_id: i32) -> std::result::Result<(), u32> {
        create_user_de_dir(user_id).map_err(|e| e.code as u32)?;
        self.user_id = user_id;
        Ok(())
    }

    /// Create adapt cloud table for certain asset db.
    fn create_adapt_cloud_table_for_specific_db(
        &self,
        db_info: &ExtDbMap,
        is_ce: bool,
    ) -> std::result::Result<(), u32> {
        let db_name = get_db_name(self.user_id, db_info, is_ce).map_err(|e| e.code as u32)?;
        let mut db = get_db_key_and_build_db(self.user_id, is_ce, &db_name)?;
        db.create_adapt_cloud_table().map_err(|e| e.code as u32)
    }

    /// Adds an asset to de db.
    fn add(&self, attributes: &ExtDbMap) -> std::result::Result<i32, u32> {
        let db_name = get_db_name(self.user_id, attributes, false).map_err(|e| e.code as u32)?;
        let mut db = Database::build_with_file_name(self.user_id, &db_name, &None).map_err(|e| e.code as u32)?;
        db.insert_datas(attributes).map_err(|e| e.code as u32)
    }

    /// Adds an asset to ce db.
    fn ce_add(&self, attributes: &ExtDbMap) -> std::result::Result<i32, u32> {
        let db_name = get_db_name(self.user_id, attributes, true).map_err(|e| e.code as u32)?;
        let db_key = get_db_key(self.user_id, true).map_err(|e| e.code as u32)?;
        let mut db = Database::build_with_file_name(self.user_id, &db_name, &db_key).map_err(|e| e.code as u32)?;
        db.insert_datas(attributes).map_err(|e| e.code as u32)
    }

    /// Adds an asset to db in asset and adapt table.
    fn add_cloud_adapt_data(
        &self, attributes: &ExtDbMap, adapt_attributes: &ExtDbMap, is_ce: bool, need_lock: bool,
    ) -> std::result::Result<i32, u32> {
        let db_name = get_db_name(self.user_id, attributes, is_ce).map_err(|e| e.code as u32)?;
        let db_key = get_db_key(self.user_id, is_ce).map_err(|e| e.code as u32)?;
        if need_lock {
            let mut db = Database::build_with_file_name(self.user_id, &db_name, &db_key).map_err(|e| e.code as u32)?;
            db.insert_cloud_adapt_data(attributes, adapt_attributes).map_err(|e| e.code as u32)
        } else {
            let mut db = Database::build_with_file_name_without_lock(self.user_id, &db_name, &db_key).map_err(|e| e.code as u32)?;
            db.insert_cloud_adapt_data_without_lock(attributes, adapt_attributes).map_err(|e| e.code as u32)
        }
    }

    /// Adds an asset with replace to de db.
    fn replace(&self, condition: &ExtDbMap, attributes: &ExtDbMap) -> std::result::Result<(), u32> {
        let db_name = get_db_name(self.user_id, attributes, false).map_err(|e| e.code as u32)?;
        let mut db = Database::build_with_file_name(self.user_id, &db_name, &None).map_err(|e| e.code as u32)?;
        db.replace_datas(condition, false, attributes).map_err(|e| e.code as u32)
    }

    /// Adds an asset with replace to ce db.
    fn ce_replace(&self, condition: &ExtDbMap, attributes: &ExtDbMap) -> std::result::Result<(), u32> {
        let db_name = get_db_name(self.user_id, attributes, true).map_err(|e| e.code as u32)?;
        let db_key = get_db_key(self.user_id, true).map_err(|e| e.code as u32)?;
        let mut db = Database::build_with_file_name(self.user_id, &db_name, &db_key).map_err(|e| e.code as u32)?;
        db.replace_datas(condition, false, attributes).map_err(|e| e.code as u32)
    }

    /// Queries de db.
    fn query(&self, attributes: &ExtDbMap) -> std::result::Result<Vec<ExtDbMap>, u32> {
        let de_dbs = asset_file_operator::de_operator::get_de_user_dbs(self.user_id).map_err(|e| e.code as u32)?;
        let mut query_data = vec![];
        for db_name in de_dbs {
            let mut db = Database::build_with_file_name(self.user_id, &db_name, &None).map_err(|e| e.code as u32)?;
            query_data.extend(db.query_datas(&vec![], attributes, None, false).map_err(|e| e.code as u32)?);
        }
        Ok(query_data)
    }

    /// Queries ce db.
    fn ce_query(&self, attributes: &ExtDbMap) -> std::result::Result<Vec<ExtDbMap>, u32> {
        let ce_dbs = asset_file_operator::ce_operator::get_ce_user_dbs(self.user_id).map_err(|e| e.code as u32)?;
        let mut query_data = vec![];
        let db_key = get_db_key(self.user_id, true).map_err(|e| e.code as u32)?;
        for db_name in ce_dbs {
            let mut db = Database::build_with_file_name(self.user_id, &db_name, &db_key).map_err(|e| e.code as u32)?;
            query_data.extend(db.query_datas(&vec![], attributes, None, false).map_err(|e| e.code as u32)?);
        }
        Ok(query_data)
    }

    fn query_target_data(
        &self,
        db_name: &str,
        columns: &[&'static str],
        sql_where: &str,
        limit: u32,
        offset: u32,
        is_ce: bool,
    ) -> std::result::Result<Vec<ExtDbMap>, u32> {
        let mut db = get_db_key_and_build_db(self.user_id, is_ce, db_name)?;
        let condition = ExtDbMap::new();
        let query_options = QueryOptions {
            offset: Some(offset),
            limit: Some(limit),
            order: None,
            order_by: None,
            amend: Some(sql_where.to_string()),
        };
        let query_data =
            db.query_datas(&columns.to_vec(), &condition, Some(&query_options), false).map_err(|e| e.code as u32)?;
        Ok(query_data)
    }

    /// Query db with attributes to a certain db. Normal, Group, CE.
    fn query_certain_db(
        &self,
        db_info: &ExtDbMap,
        attributes: &ExtDbMap,
        query_options: &ExtDbMap,
        is_ce: bool,
        is_filter_sync: bool,
    ) -> std::result::Result<Vec<ExtDbMap>, u32> {
        let db_name = get_db_name(self.user_id, db_info, is_ce).map_err(|e| e.code as u32)?;
        let mut db = get_db_key_and_build_db(self.user_id, is_ce, &db_name)?;
        db.query_datas(&vec![], attributes, Some(&get_query_options(query_options)), is_filter_sync).map_err(|e| e.code as u32)
    }

    /// Query db with attributes to a certain db. Normal, Group, CE.
    fn query_certain_db_with_connect_table(
        &self,
        db_info: &ExtDbMap,
        attributes: &ExtDbMap,
        is_ce: bool,
        need_lock: bool,
    ) -> std::result::Result<Vec<ExtDbMap>, u32> {
        let db_name = get_db_name(self.user_id, db_info, is_ce).map_err(|e| e.code as u32)?;
        let db_key = get_db_key(self.user_id, is_ce).map_err(|e| e.code as u32)?;
        if need_lock {
            let mut db = Database::build_with_file_name(self.user_id, &db_name, &db_key).map_err(|e| e.code as u32)?;
            db.query_datas_with_connect_table(&vec![], attributes, None, false).map_err(|e| e.code as u32)
        } else {
            let mut db = Database::build_with_file_name_without_lock(self.user_id, &db_name, &db_key).map_err(|e| e.code as u32)?;
            db.query_datas_with_connect_table_without_lock(&vec![], attributes, None, false).map_err(|e| e.code as u32)
        }
    }

    /// Removes an asset from de db.
    fn remove(&self, attributes: &ExtDbMap) -> std::result::Result<i32, u32> {
        let de_dbs = asset_file_operator::de_operator::get_de_user_dbs(self.user_id).map_err(|e| e.code as u32)?;
        let mut total_remove_count = 0;
        for db_name in de_dbs {
            let mut db = Database::build_with_file_name(self.user_id, &db_name, &None).map_err(|e| e.code as u32)?;
            total_remove_count += db.delete_datas(attributes, None, false).map_err(|e| e.code as u32)?;
        }
        Ok(total_remove_count)
    }

    /// Removes an asset from ce db.
    fn ce_remove(&self, attributes: &ExtDbMap) -> std::result::Result<i32, u32> {
        let ce_dbs = asset_file_operator::ce_operator::get_ce_user_dbs(self.user_id).map_err(|e| e.code as u32)?;
        let mut total_remove_count = 0;
        let db_key = get_db_key(self.user_id, true).map_err(|e| e.code as u32)?;
        for db_name in ce_dbs {
            let mut db = Database::build_with_file_name(self.user_id, &db_name, &db_key).map_err(|e| e.code as u32)?;
            total_remove_count += db.delete_datas(attributes, None, false).map_err(|e| e.code as u32)?;
        }
        Ok(total_remove_count)
    }

    /// Removes an asset from a certain db. Normal, Group, CE.
    fn remove_certain_db(
        &self,
        db_info: &ExtDbMap,
        attributes: &ExtDbMap,
        is_ce: bool,
    ) -> std::result::Result<i32, u32> {
        let db_name = get_db_name(self.user_id, db_info, is_ce).map_err(|e| e.code as u32)?;
        let mut db = get_db_key_and_build_db(self.user_id, is_ce, &db_name)?;
        db.delete_datas(attributes, None, false).map_err(|e| e.code as u32)
    }

    /// Removes assets from de db with sepcific condition.
    fn remove_with_specific_cond(
        &self,
        specific_cond: &str,
        condition_value: &[Value],
    ) -> std::result::Result<i32, u32> {
        let de_dbs = asset_file_operator::de_operator::get_de_user_dbs(self.user_id).map_err(|e| e.code as u32)?;
        let mut total_remove_count = 0;
        for db_name in de_dbs {
            let mut db = Database::build_with_file_name(self.user_id, &db_name, &None).map_err(|e| e.code as u32)?;
            total_remove_count +=
                db.delete_specific_condition_datas(specific_cond, condition_value).map_err(|e| e.code as u32)?;
        }
        Ok(total_remove_count)
    }

    /// Removes assets from ce db with sepcific condition.
    fn ce_remove_with_specific_cond(
        &self,
        specific_cond: &str,
        condition_value: &[Value],
    ) -> std::result::Result<i32, u32> {
        let ce_dbs = asset_file_operator::ce_operator::get_ce_user_dbs(self.user_id).map_err(|e| e.code as u32)?;
        let mut total_remove_count = 0;
        let db_key = get_db_key(self.user_id, true).map_err(|e| e.code as u32)?;
        for db_name in ce_dbs {
            let mut db = Database::build_with_file_name(self.user_id, &db_name, &db_key).map_err(|e| e.code as u32)?;
            total_remove_count +=
                db.delete_specific_condition_datas(specific_cond, condition_value).map_err(|e| e.code as u32)?;
        }
        Ok(total_remove_count)
    }

    /// Remove an asset to db in asset and adapt table.
    fn remove_cloud_adapt_data(
        &self,
        db_info: &ExtDbMap,
        attributes: Option<&ExtDbMap>,
        adapt_attributes: Option<&ExtDbMap>,
        is_ce: bool,
        need_lock: bool,
    ) -> std::result::Result<i32, u32> {
        let db_name = get_db_name(self.user_id, db_info, is_ce).map_err(|e| e.code as u32)?;
        let db_key = get_db_key(self.user_id, is_ce).map_err(|e| e.code as u32)?;
        if need_lock {
            let mut db = Database::build_with_file_name(self.user_id, &db_name, &db_key).map_err(|e| e.code as u32)?;
            db.delete_adapt_data(attributes, adapt_attributes).map_err(|e| e.code as u32)
        } else {
            let mut db = Database::build_with_file_name_without_lock(self.user_id, &db_name, &db_key).map_err(|e| e.code as u32)?;
            db.delete_adapt_data_without_lock(attributes, adapt_attributes).map_err(|e| e.code as u32)
        }
    }

    /// Updates the attributes of an asset in de db.
    fn update(&self, attributes: &ExtDbMap, attrs_to_update: &ExtDbMap) -> std::result::Result<i32, u32> {
        let de_dbs = asset_file_operator::de_operator::get_de_user_dbs(self.user_id).map_err(|e| e.code as u32)?;
        let mut total_update_count = 0;
        for db_name in de_dbs {
            let mut db = Database::build_with_file_name(self.user_id, &db_name, &None).map_err(|e| e.code as u32)?;
            total_update_count += db.update_datas(attributes, false, attrs_to_update).map_err(|e| e.code as u32)?;
        }
        Ok(total_update_count)
    }

    /// Updates the attributes of an asset in ce db.
    fn ce_update(&self, attributes: &ExtDbMap, attrs_to_update: &ExtDbMap) -> std::result::Result<i32, u32> {
        let ce_dbs = asset_file_operator::ce_operator::get_ce_user_dbs(self.user_id).map_err(|e| e.code as u32)?;
        let mut total_update_count = 0;
        let db_key = get_db_key(self.user_id, true).map_err(|e| e.code as u32)?;
        for db_name in ce_dbs {
            let mut db = Database::build_with_file_name(self.user_id, &db_name, &db_key).map_err(|e| e.code as u32)?;
            total_update_count += db.update_datas(attributes, false, attrs_to_update).map_err(|e| e.code as u32)?;
        }
        Ok(total_update_count)
    }

    fn get_certain_db_lock(
        &self,
        db_info: &ExtDbMap,
        is_ce: bool,
    ) -> std::result::Result<Arc<Mutex<i32>>, u32> {
        let db_name = get_db_name(self.user_id, db_info, is_ce).map_err(|e| e.code as u32)?;
        let db = get_db_key_and_build_db(self.user_id, is_ce, &db_name)?;
        let lock = db.get_db_lock().map_err(|e| e.code as u32)?;
        Ok(lock)
    }

    /// Returns the storage path for de db.
    fn get_storage_path(&self) -> String {
        get_path()
    }

    /// Increase count
    fn increase_count(&self) {
        let counter = Counter::get_instance();
        counter.lock().unwrap().increase_count();
    }

    /// Decrease count
    fn decrease_count(&self) {
        let counter = Counter::get_instance();
        counter.lock().unwrap().decrease_count();
    }

    /// Add task
    fn add_task(&self, handle: JoinHandle<()>) {
        let task_manager = TaskManager::get_instance();
        task_manager.lock().unwrap().push_task(handle);
    }

    /// is ce upgrade
    fn is_ce_upgrade(&self, user_id: i32) -> bool {
        match get_file_content(user_id) {
            Ok(res) => res.ce_upgrade.is_some(),
            Err(_e) => false
        }
    }

    /// store key-value into DataShare
    fn store_key_value(&self, user_id: i32, column_key: &str, column_value: i32) -> bool {
        match CString::new(column_key) {
            Ok(key) => unsafe { StoreKeyValue(user_id, key.as_ptr(), column_value) },
            Err(e) => {
                loge!("[store_key_value] create CString from {} failed, error:{:?}", column_key, e);
                false
            }
        }
    }
    
    /// query value in DataShare
    fn query_value(&self, user_id: i32, column_key: &str) -> Result<i32> {
        match CString::new(column_key) {
            Ok(key) => {
                let mut column_value: i32 = DATASHARE_FAIL;
                let result = unsafe { QueryValue(user_id, key.as_ptr(), &mut column_value) };
                logi!("[query_value] result:{}, key:{}, value:{}", result, column_key, column_value);
                if result == DATASHARE_SUCCESS {
                    Ok(column_value)
                } else {
                    macros_lib::log_throw_error!(macros_lib::hisysevent::function!(),
                        ErrCode::DatabaseError, "[query_value] query settingsdata.db failed")
                }
            },
            Err(_) => {
                macros_lib::log_throw_error!(macros_lib::hisysevent::function!(),
                    ErrCode::InvalidArgument, "[query_value] create CString failed")
            }
        }
    }

    /// Check whether the device is locked.
    fn is_device_locked(&self) -> std::result::Result<bool, u32> {
        let mut is_device_locked: bool = true;
        let result = unsafe { IsDeviceLocked(self.user_id, &mut is_device_locked) };
        if result == SUCCESS {
            Ok(is_device_locked)
        } else {
            Err(result as u32)
        }
    }

}