* Copyright (c) 2023-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 ipc::parcel::MsgParcel;
use samgr::manage::SystemAbilityManager;
use std::{
fs,
ffi::CStr,
time::{Duration, Instant},
sync::{Mutex, MutexGuard, RwLock},
};
use system_ability_fwk::{
ability::{Ability, Handler},
cxx_share::SystemAbilityOnDemandReason,
};
use ylong_runtime::builder::RuntimeBuilder;
use ylong_json::JsonValue;
use lazy_static::lazy_static;
use asset_common::{AutoCounter, CallingInfo, ConstAssetBlob, ConstAssetBlobArray, Counter, TaskManager,
OwnerType, Group, MutAssetBlob, MutAssetBlobArrayChangeable, ProcessInfo
};
use asset_crypto_manager::{crypto_manager::CryptoManager, db_key_operator::get_db_key};
use asset_db_operator::{database_file_upgrade::check_and_split_db, database::{preload_db, clear_db_map}};
use asset_definition::{macros_lib, AssetMap, ErrCode, Result, SyncResult};
use asset_file_operator::{common::DE_ROOT_PATH, de_operator::create_user_de_dir};
use asset_ipc::{SA_ID, deserialize};
use asset_log::{loge, logi};
use asset_plugin::asset_plugin::{AssetContext, AssetPlugin};
mod common_event;
mod data_size_mod;
mod operations;
mod stub;
mod sys_event;
mod trace_scope;
mod upgrade_operator;
mod upgrade_ce;
use sys_event::{upload_system_event, upload_batch_system_event};
use trace_scope::TraceScope;
use crate::data_size_mod::handle_data_size_upload;
struct AssetAbility;
lazy_static! {
static ref UPGRADE_CE_MUTEX: RwLock<()> = RwLock::new(());
}
trait WantParser<T> {
fn parse(&self) -> Result<T>;
}
struct PackageInfo {
user_id: i32,
app_index: i32,
app_id: String,
developer_id: Option<String>,
group_ids: Option<Vec<String>>,
bundle_name: String,
}
#[repr(C)]
struct PackageInfoFfi {
user_id: i32,
app_index: i32,
owner: ConstAssetBlob,
developer_id: ConstAssetBlob,
group_ids: ConstAssetBlobArray,
bundle_name: ConstAssetBlob,
}
static DELAYED_UNLOAD_TIME_IN_SEC: i32 = 20;
static DELAYED_UNLOAD_TIME_FOR_COMMON_EVENT_IN_SEC: i32 = 5;
static SEC_TO_MILLISEC: i32 = 1000;
const RSS_SA_EXTENSION: &str = "RssSaExtension";
const PREPARE_FOR_BUNDLE: u32 = 27;
const START_STATUS: i32 = 1;
const DEAD_STATUS: i32 = 0;
const MEMORY_MANAGER_SA_ID: i32 = 1909;
#[derive(PartialEq)]
enum SaStatus {
Active,
Idle
}
#[derive(PartialEq)]
enum CriticalStatus {
NotInit = -1,
SetTrue = 1,
SetFalse = 0,
}
struct MemoryMgrInfo {
sa_status: SaStatus,
notify_status: bool,
critical_status: CriticalStatus,
}
impl MemoryMgrInfo {
pub(crate) fn build() -> Self {
Self { sa_status: SaStatus::Active, notify_status: false, critical_status: CriticalStatus::NotInit }
}
pub(crate) fn set_notify_status(&mut self, status: bool) {
self.notify_status = status
}
pub(crate) fn set_sa_status(&mut self, sa_status: SaStatus) {
self.sa_status = sa_status
}
pub(crate) fn set_critical_status(&mut self, critical_status: CriticalStatus) {
self.critical_status = critical_status
}
pub(crate) fn get_notify_status(&self) -> bool {
self.notify_status
}
pub(crate) fn get_sa_status(&self) -> &SaStatus {
&self.sa_status
}
pub(crate) fn get_critical_status(&self) -> &CriticalStatus {
&self.critical_status
}
}
lazy_static::lazy_static! {
static ref HAS_NOTIFY_MEMORY_MGR_MUTEX: Mutex<MemoryMgrInfo> = Mutex::new(MemoryMgrInfo::build());
}
extern "C" {
fn CheckMemoryMgr() -> bool;
fn NotifyStatus(size: i32) -> i32;
fn SetCritical(critical: bool) -> i32;
}
fn set_notify_info(lock: &mut MutexGuard<MemoryMgrInfo>, notify_info: i32) {
if lock.get_notify_status() { return; }
unsafe { NotifyStatus(notify_info); }
lock.set_notify_status(true);
}
fn set_critical_info(lock: &mut MutexGuard<MemoryMgrInfo>, critical: bool) {
match lock.get_critical_status() {
CriticalStatus::NotInit => {
if !critical {
loge!("[FATAL]func use error");
return;
}
unsafe { SetCritical(critical); }
lock.set_critical_status(CriticalStatus::SetTrue);
},
CriticalStatus::SetTrue => {
if critical {
return;
}
unsafe { SetCritical(critical); }
lock.set_critical_status(CriticalStatus::SetFalse);
},
CriticalStatus::SetFalse => {
if !critical {
return;
}
unsafe { SetCritical(critical); }
lock.set_critical_status(CriticalStatus::SetTrue);
},
}
}
impl PackageInfo {
fn developer_id(&self) -> &Option<String> {
&self.developer_id
}
fn group_ids(&self) -> &Option<Vec<String>> {
&self.group_ids
}
}
pub(crate) fn unload_sa() {
unload_sa_with_delay(DELAYED_UNLOAD_TIME_IN_SEC);
}
pub(crate) fn unload_sa_with_delay(delay_sec: i32) {
ylong_runtime::spawn(async move {
loop {
ylong_runtime::time::sleep(Duration::from_secs(delay_sec as u64)).await;
let crypto_manager = CryptoManager::get_instance();
let max_crypto_expire_duration = crypto_manager.lock().unwrap().max_crypto_expire_duration();
if max_crypto_expire_duration > 0 {
continue;
}
let counter = Counter::get_instance();
if counter.lock().unwrap().count() > 0 {
continue;
}
let task_manager = TaskManager::get_instance();
if !task_manager.lock().unwrap().is_empty() {
continue;
}
SystemAbilityManager::unload_system_ability(SA_ID);
break;
}
});
}
impl Ability for AssetAbility {
fn on_start_with_reason(&self, reason: SystemAbilityOnDemandReason, handler: Handler) {
logi!("Start asset, reason_id: {:?}", reason.reason_id);
if let Err(e) = RuntimeBuilder::new_multi_thread().worker_num(1).max_blocking_pool_size(1).build_global() {
loge!("[WARNING]Ylong new global thread failed! {}", e);
};
let func_name = macros_lib::hisysevent::function!();
let start = Instant::now();
let _trace = TraceScope::trace(func_name);
let calling_info = CallingInfo::new_self();
let _ = upload_system_event(start_service(handler), &calling_info, start, func_name, &AssetMap::new());
if let Err(e) = handle_data_size_upload() {
loge!("Failed to handle data upload: {}", e);
}
common_event::handle_common_event(reason);
}
fn on_system_ability_load_event(&self, said: i32, device_id: String) {
logi!("Receive service load event, said:{}, device_id:{}", said, &device_id);
if said != MEMORY_MANAGER_SA_ID { return; }
let mut lock = HAS_NOTIFY_MEMORY_MGR_MUTEX.lock().unwrap();
set_notify_info(&mut lock, START_STATUS);
if (*lock).get_sa_status() == &SaStatus::Active {
set_critical_info(&mut lock, true);
}
}
fn on_system_ability_remove_event(&self, said: i32, device_id: String) {
loge!("[ERROR]Receive service unload event, said:{}, device_id:{}", said, &device_id);
if said == MEMORY_MANAGER_SA_ID {
let mut lock = HAS_NOTIFY_MEMORY_MGR_MUTEX.lock().unwrap();
(*lock).set_notify_status(false);
(*lock).set_critical_status(CriticalStatus::NotInit);
}
}
fn on_active(&self, reason: SystemAbilityOnDemandReason) {
let mut lock = HAS_NOTIFY_MEMORY_MGR_MUTEX.lock().unwrap();
(*lock).set_sa_status(SaStatus::Active);
logi!("Asset on_active.");
if let Err(e) = handle_data_size_upload() {
loge!("Failed to handle data upload: {}", e);
}
common_event::handle_common_event(reason);
unsafe {
if !CheckMemoryMgr() { return; }
set_notify_info(&mut lock, START_STATUS);
set_critical_info(&mut lock, true);
}
}
fn on_idle(&self, _reason: SystemAbilityOnDemandReason) -> i32 {
clear_db_map();
let crypto_manager = CryptoManager::get_instance();
let max_crypto_expire_duration = crypto_manager.lock().unwrap().max_crypto_expire_duration();
if max_crypto_expire_duration > 0 {
logi!("Asset on idle not success, delay time: {}s", max_crypto_expire_duration);
return max_crypto_expire_duration as i32 * SEC_TO_MILLISEC;
}
let counter = Counter::get_instance();
if counter.lock().unwrap().count() > 0 {
logi!(
"Asset on idle not success for use_account: {}, delay time: {}s",
counter.lock().unwrap().count(),
DELAYED_UNLOAD_TIME_IN_SEC
);
return DELAYED_UNLOAD_TIME_IN_SEC * SEC_TO_MILLISEC;
}
let mut lock = HAS_NOTIFY_MEMORY_MGR_MUTEX.lock().unwrap();
(*lock).set_sa_status(SaStatus::Idle);
logi!("Asset on_idle.");
unsafe {
if !CheckMemoryMgr() { return 0; }
set_notify_info(&mut lock, START_STATUS);
set_critical_info(&mut lock, true);
set_critical_info(&mut lock, false);
}
0
}
fn on_stop(&self) {
logi!("Asset on_stop");
let counter = Counter::get_instance();
counter.lock().unwrap().stop();
common_event::unsubscribe();
let lock = HAS_NOTIFY_MEMORY_MGR_MUTEX.lock().unwrap();
if !(*lock).get_notify_status() { return; }
unsafe { NotifyStatus(DEAD_STATUS); }
}
fn on_extension(&self, extension: String, data: &mut MsgParcel, reply: &mut MsgParcel) -> i32 {
logi!("Asset on_extension, extension is {}", extension);
if extension == RSS_SA_EXTENSION {
match on_preload_extension(data) {
Ok(()) => logi!("process preload extension event success."),
Err(_) => loge!("process preload extension event failed."),
}
} else if let Ok(load) = AssetPlugin::get_instance().load_plugin() {
match load.on_sa_extension(extension, data, reply) {
Ok(()) => logi!("process sa extension event success."),
Err(code) => loge!("process sa extension event failed, code: {}", code),
};
}
logi!("Asset on_extension end");
0
}
}
fn get_db_key_and_preload_db(calling_info: &CallingInfo, is_ce: bool) -> Result<()> {
let db_key = get_db_key(calling_info.user_id(), is_ce)?;
preload_db(calling_info, db_key)
}
fn get_value_from_json(json_str: String, key: &str) -> String {
let json = match JsonValue::from_text(json_str) {
Ok(json) => json,
Err(_) => {
loge!("parse json from String failed.");
JsonValue::from_text("{}").unwrap()
}
};
if json.try_as_object().unwrap().is_empty() {
loge!("");
return "".to_string();
}
let value: &JsonValue = &json[key];
if value == &JsonValue::Null {
return "".to_string();
}
value.to_compact_string().unwrap().replace('\"', "")
}
extern "C" {
fn GetCallingHapGroups(uid: u64, group_ids: *mut MutAssetBlobArrayChangeable, developer_id: *mut MutAssetBlob) -> i32;
}
fn construct_group_calling_infos(user_id: i32, owner: Vec<u8>,uid: u64) -> Vec<CallingInfo> {
let mut group_id_blobs: Vec<Vec<u8>> = Vec::new();
let mut group_id_blobs_ptr_record = Vec::new();
for _i in 0..512 {
let mut data_blob = vec![0u8; 128];
let data_blob_ptr = MutAssetBlob { size: data_blob.len() as u32, data: data_blob.as_mut_ptr() };
group_id_blobs_ptr_record.push(data_blob_ptr);
group_id_blobs.push(data_blob);
}
let mut developer_id = vec![0; 128];
let mut the_group_ids = MutAssetBlobArrayChangeable {
size: group_id_blobs_ptr_record.len() as u32, blobs: group_id_blobs_ptr_record.as_mut_ptr()
};
let mut the_developer_id = MutAssetBlob { size: developer_id.len() as u32, data: developer_id.as_mut_ptr() };
let use_group_ids = match unsafe { GetCallingHapGroups(uid, &mut the_group_ids, &mut the_developer_id) } {
0 => {
let mut group_id_list = Vec::with_capacity(the_group_ids.size as usize);
developer_id.truncate(the_developer_id.size as usize);
group_id_blobs.truncate(the_group_ids.size as usize);
for (idx, group_id) in group_id_blobs.iter().enumerate() {
let mut use_group_id = group_id.clone();
unsafe { use_group_id.truncate((*the_group_ids.blobs.add(idx as usize)).size as usize); }
group_id_list.push(use_group_id);
}
group_id_list
},
error => {
loge!("[FATAL]Get GetUninstallGroups failed, res is {}.", error);
return vec![];
},
};
let mut calling_infos = Vec::with_capacity(use_group_ids.len());
for group_id in use_group_ids {
calling_infos.push(CallingInfo::new(
user_id,
OwnerType::HapGroup,
owner.clone(),
Some(Group { developer_id: developer_id.clone(), group_id: group_id.clone() }),
));
}
calling_infos
}
fn on_preload_extension(data: &mut MsgParcel) -> Result<()> {
let res_type = deserialize::<u32>(data)?;
if res_type != PREPARE_FOR_BUNDLE {
return macros_lib::log_throw_error!(macros_lib::hisysevent::function!(),
ErrCode::InvalidArgument, "res_type is not PREPARE_FOR_BUNDLE");
}
let _value = deserialize::<i64>(data)?;
let json_str = deserialize::<String>(data)?;
let value = get_value_from_json(json_str, "uid");
let uid = match value.parse::<u64>() {
Ok(num) => num,
Err(_) => return macros_lib::log_throw_error!(macros_lib::hisysevent::function!(),
ErrCode::InvalidArgument, "parse uid from json value failed!"),
};
let process_info = ProcessInfo::build(None, Some(uid), true)?;
let calling_info = CallingInfo::build(None, &process_info);
let _ = get_db_key_and_preload_db(&calling_info, false);
let _ = get_db_key_and_preload_db(&calling_info, true);
let group_calling_infos = construct_group_calling_infos(
calling_info.user_id(), calling_info.owner_info().clone(), uid
);
for group_calling_info in &group_calling_infos {
let _ = get_db_key_and_preload_db(group_calling_info, false);
let _ = get_db_key_and_preload_db(group_calling_info, true);
}
Ok(())
}
async fn execute_upgrade_process() {
match upgrade_process() {
Ok(()) => (),
Err(e) => loge!("upgrade failed, err:[{}]", e.code),
}
}
fn upgrade_process() -> Result<()> {
let _counter_user = AutoCounter::new();
for entry in fs::read_dir(DE_ROOT_PATH)? {
let entry = entry?;
if let Ok(user_id) = entry.file_name().to_string_lossy().parse::<i32>() {
check_and_split_db(user_id)?;
}
}
Ok(())
}
fn start_service(handler: Handler) -> Result<()> {
let asset_plugin = AssetPlugin::get_instance();
match asset_plugin.load_plugin() {
Ok(loader) => {
let _tr = loader.init(Box::new(AssetContext { user_id: 0 }));
},
Err(_) => loge!("load plugin failed."),
}
if !handler.publish(AssetService::new(handler.clone())) {
return macros_lib::log_throw_error!(macros_lib::hisysevent::function!(),
ErrCode::IpcError, "Asset publish stub object failed");
};
common_event::subscribe();
handler.add_system_ability_listen(MEMORY_MANAGER_SA_ID);
let handle = ylong_runtime::spawn(execute_upgrade_process());
let task_manager = TaskManager::get_instance();
task_manager.lock().unwrap().push_task(handle);
Ok(())
}
#[used]
#[link_section = ".init_array"]
static A: extern "C" fn() = {
extern "C" fn init() {
let Some(sa) = AssetAbility.build_system_ability(SA_ID, true) else {
loge!("Create Asset service failed.");
return;
};
sa.register();
}
init
};
extern "C" {
fn GetCeUpgradeInfo() -> *const u8;
}
pub(crate) fn get_ce_upgrade_info() -> &'static [u8] {
let info = unsafe { GetCeUpgradeInfo() };
if !info.is_null() {
let c_str = unsafe { CStr::from_ptr(info as _) };
if let Ok(result) = c_str.to_str() {
return result.as_bytes()
}
}
&[]
}
struct AssetService {
system_ability: system_ability_fwk::ability::Handler,
}
macro_rules! execute {
($func:path, $calling_info:expr, $first_arg:expr) => {{
let func_name = macros_lib::hisysevent::function!();
let start = Instant::now();
let _trace = TraceScope::trace(func_name);
create_user_de_dir($calling_info.user_id())?;
let ce_upgrade_info = get_ce_upgrade_info();
if ce_upgrade_info == $calling_info.owner_info() {
let _rwlock = UPGRADE_CE_MUTEX.read().unwrap();
upload_system_event($func($calling_info, $first_arg), $calling_info, start, func_name, $first_arg)
} else {
upload_system_event($func($calling_info, $first_arg), $calling_info, start, func_name, $first_arg)
}
}};
($func:path, $calling_info:expr, $first_arg:expr, $second_arg:expr) => {{
let func_name = macros_lib::hisysevent::function!();
let start = Instant::now();
let _trace = TraceScope::trace(func_name);
create_user_de_dir($calling_info.user_id())?;
let ce_upgrade_info = get_ce_upgrade_info();
if ce_upgrade_info == $calling_info.owner_info() {
let _rwlock = UPGRADE_CE_MUTEX.read().unwrap();
upload_system_event(
$func($calling_info, $first_arg, $second_arg), $calling_info, start, func_name, $first_arg)
} else {
upload_system_event(
$func($calling_info, $first_arg, $second_arg), $calling_info, start, func_name, $first_arg)
}
}};
}
macro_rules! execute_batch {
($func:path, $calling_info:expr, $first_arg:expr) => {{
let func_name = macros_lib::hisysevent::function!();
let start = Instant::now();
let _trace = TraceScope::trace(func_name);
create_user_de_dir($calling_info.user_id())?;
let ce_upgrade_info = get_ce_upgrade_info();
if ce_upgrade_info == $calling_info.owner_info() {
let _rwlock = UPGRADE_CE_MUTEX.read().unwrap();
upload_batch_system_event($func($calling_info, $first_arg), $calling_info, start, func_name, $first_arg)
} else {
upload_batch_system_event($func($calling_info, $first_arg), $calling_info, start, func_name, $first_arg)
}
}};
($func:path, $calling_info:expr, $first_arg:expr, $second_arg:expr) => {{
let func_name = macros_lib::hisysevent::function!();
let start = Instant::now();
let _trace = TraceScope::trace(func_name);
create_user_de_dir($calling_info.user_id())?;
let ce_upgrade_info = get_ce_upgrade_info();
if ce_upgrade_info == $calling_info.owner_info() {
let _rwlock = UPGRADE_CE_MUTEX.read().unwrap();
upload_batch_system_event(
$func($calling_info, $first_arg, $second_arg), $calling_info, start, func_name, $first_arg)
} else {
upload_batch_system_event(
$func($calling_info, $first_arg, $second_arg), $calling_info, start, func_name, $first_arg)
}
}};
}
impl AssetService {
pub(crate) fn new(handler: system_ability_fwk::ability::Handler) -> Self {
Self { system_ability: handler }
}
fn add(&self, calling_info: &CallingInfo, attributes: &AssetMap) -> Result<()> {
execute!(operations::add, calling_info, attributes)
}
fn remove(&self, calling_info: &CallingInfo, query: &AssetMap) -> Result<()> {
execute!(operations::remove, calling_info, query)
}
fn update(&self, calling_info: &CallingInfo, query: &AssetMap, attributes_to_update: &AssetMap) -> Result<()> {
execute!(operations::update, calling_info, query, attributes_to_update)
}
fn pre_query(&self, calling_info: &CallingInfo, query: &AssetMap) -> Result<Vec<u8>> {
execute!(operations::pre_query, calling_info, query)
}
fn query(&self, calling_info: &CallingInfo, query: &AssetMap) -> Result<Vec<AssetMap>> {
execute!(operations::query, calling_info, query)
}
fn post_query(&self, calling_info: &CallingInfo, query: &AssetMap) -> Result<()> {
execute!(operations::post_query, calling_info, query)
}
fn query_sync_result(&self, calling_info: &CallingInfo, query: &AssetMap) -> Result<SyncResult> {
execute!(operations::query_sync_result, calling_info, query)
}
fn batch_add(&self, calling_info: &CallingInfo, attributes_array: &[AssetMap]) -> Result<Vec<(u32, u32)>> {
execute_batch!(operations::batch_add, calling_info, attributes_array)
}
fn batch_remove(&self, calling_info: &CallingInfo, attributes_array: &[AssetMap]) -> Result<()> {
execute_batch!(operations::batch_remove, calling_info, attributes_array)
}
fn batch_update(
&self, calling_info: &CallingInfo,
attributes_array: &[AssetMap],
attributes_to_update_array: &[AssetMap]
) -> Result<Vec<(u32, u32)>> {
execute_batch!(operations::batch_update, calling_info, attributes_array, attributes_to_update_array)
}
}
#[cfg(feature = "AssetTest")]
pub mod ut_core_service_lib_stub {
include!{"../../../../test/asset/unittest/ut_test/services/core_service/test_stub/ut_core_service_lib_stub.rs"}
}