* 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;
#[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) }
}
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()
}
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))
}
}
pub fn unload_plugin(&self) {
let _guard = ASSET_PLUGIN_LOCK.lock().unwrap();
if self.lib.borrow().is_some() {
*self.lib.borrow_mut() = None;
}
}
}
#[repr(C)]
pub struct AssetContext {
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 {
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(())
}
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)
}
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)
}
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)
}
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)
}
}
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)
}
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)
}
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)
}
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)
}
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)
}
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)
}
}
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)
}
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)
}
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)
}
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)
}
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)
}
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)
}
}
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)
}
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)
}
fn get_storage_path(&self) -> String {
get_path()
}
fn increase_count(&self) {
let counter = Counter::get_instance();
counter.lock().unwrap().increase_count();
}
fn decrease_count(&self) {
let counter = Counter::get_instance();
counter.lock().unwrap().decrease_count();
}
fn add_task(&self, handle: JoinHandle<()>) {
let task_manager = TaskManager::get_instance();
task_manager.lock().unwrap().push_task(handle);
}
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
}
}
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
}
}
}
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")
}
}
}
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)
}
}
}