/*
* Copyright (c) Huawei Technologies Co., Ltd. 2024-2025. All rights reserved.
* 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.
*/
// import appLock from '@hms.security.appLock';
import { taskpool } from '@kit.ArkTS';
import { systemDateTime } from '@kit.BasicServicesKit';
import bundleManager from '@ohos.bundle.bundleManager';
import launcherBundleResource from '@ohos.bundle.launcherBundleManager';
import storageStatistics from '@ohos.file.storageStatistics';
import storage from '@ohos.file.storageStatistics';
// import migrate from '@ohos.migrate';
import usageStatistics from '@ohos.resourceschedule.usageStatistics';
import { AppListLoader } from '@ohos/settings.application/src/main/ets/AppListLoader';
import { AppEntry } from '@ohos/settings.application/src/main/ets/AppModel';
import { AppUtils } from '@ohos/settings.application/src/main/ets/AppUtils';
import { DeviceUtil } from '@ohos/settings.common/src/main/ets/utils/BaseUtils';
import { CheckEmptyUtils } from '@ohos/settings.common/src/main/ets/utils/CheckEmptyUtils';
import { DeliverUtil } from '@ohos/settings.common/src/main/ets/utils/DeliverUtil';
import { LogUtil } from '@ohos/settings.common/src/main/ets/utils/LogUtil';
import { SettingsDataUtils } from '@ohos/settings.common/src/main/ets/utils/SettingsDataUtils';
import {
DEFAULT_DATA_SIZE,
LONGEST_UNUSED_TIME,
MAIN_APP_INDEX,
NoGetBundleStatsFlag,
StorageEventConstant
} from '../constant/StorageConstant';
import { BundleStats } from '../model/BundleStats';
import { LocalBackupModel, StorageDataManager } from '../model/StorageDataManager';
import {
getAllBundleInfoAsync,
getLauncherBundleListAsync,
getLockedAppsAsync,
getUsedAppInMonthMapAsync
} from './StorageAppUtil';
/* instrument ignore file */
const TAG = 'StorageUtil'
const DAY_TIME_MILLIS: number = 1000 * 60 * 60 * 24;
export const INVALID_DATA_SIZE: number = -1;
/**
* 存储工具类
*
* @since 2024-11-22
*/
export class StorageUtil {
static async getCacheByName(packageName: string, appIndex?: number): Promise<number> {
try {
let bundleStats = await StorageUtil.getBundleStats(packageName, appIndex,
NoGetBundleStatsFlag.GET_BUNDLE_WITHOUT_INSTALL_SIZE | NoGetBundleStatsFlag.GET_BUNDLE_WITHOUT_DATA_SIZE);
LogUtil.showInfo(TAG, `packageName: ${packageName}, appIndex: ${appIndex}, cacheSize: ${bundleStats?.cacheSize}`);
return bundleStats ? bundleStats.cacheSize : DEFAULT_DATA_SIZE;
} catch (err) {
LogUtil.showError(TAG, `getCacheByName err: ${err?.message}, ${err?.code}`);
}
return DEFAULT_DATA_SIZE;
}
static async getAllDataByName(packageName: string, appIndex?: number): Promise<BundleStats> {
let bundleStats: BundleStats = new BundleStats();
try {
let res: storageStatistics.BundleStats | undefined =
await StorageUtil.getBundleStats(packageName, appIndex, NoGetBundleStatsFlag.GET_BUNDLE_WITHOUT_CACHE_SIZE);
if (res) {
bundleStats.appSize = String(res.appSize);
bundleStats.dataSize = String(res.dataSize);
bundleStats.cacheSize = String(res.cacheSize);
bundleStats.totalSize = String(res.appSize + res.dataSize);
return bundleStats;
}
} catch (err) {
LogUtil.showError(TAG, `getAllDataByName err: ${err?.message}, ${err?.code}`);
}
return bundleStats;
}
/**
* get migration data size
*/
static async getMigrationDataSize(): Promise<number> {
LogUtil.info(`${TAG} get migration data size start`);
let dataSize: number = 0;
try {
// dataSize = await migrate.getMigrator().getMigrationDataSize();
} catch (err) {
LogUtil.error(`${TAG} get migration data size error code: ${err?.code}, message: ${err?.message}`);
}
LogUtil.info(`${TAG} get migration data size end`);
return dataSize;
}
/**
* 时间比较,计算当前时间与传入时间的差,以天为单位
*/
static getDaysByTimeMillis(timeMillis: number): number {
let nowTime: number = systemDateTime.getTime();
let timeDiffs: number = nowTime - timeMillis;
let days = Math.floor(timeDiffs / DAY_TIME_MILLIS);
return days;
}
/**
* get backUp data size
*/
static refreshBackUpDataSize(): void {
let localBackupInfo = SettingsDataUtils.getSecureValue(StorageEventConstant.LOCAL_BACKUP_INFORMATION, '');
if (!localBackupInfo) {
LogUtil.error(`${TAG} local backup information is empty`);
return;
}
try {
let localBackupModel: LocalBackupModel = JSON.parse(localBackupInfo) as LocalBackupModel;
if (localBackupModel == null) {
LogUtil.error(`${TAG} local backup model is null`);
return;
}
StorageDataManager.getInstance().setLocalBackupModel(localBackupModel);
LogUtil.info(`${TAG} get local backup data end`);
} catch (err) {
LogUtil.error(`${TAG} localBackupInfo JSON error code: ${err?.code}, message: ${err?.message}`);
}
}
/**
* 获取一个月内的已使用应用列表,统计时间段为30天前0点-当天
*
* @returns 一个月内的已使用应用列表
*/
public static async getUsedAppInMonthMap(): Promise<Map<number, string[]>> {
LogUtil.info(`${TAG} getUsedAppInMonthList`);
let currentTime = new Date().getTime();
let res: Record<string, Array<usageStatistics.BundleStatsInfo>> = {};
try {
// 统计30天前0点-当天的使用数据
res = await usageStatistics.queryAppStatsInfos(currentTime - LONGEST_UNUSED_TIME, currentTime);
} catch (err) {
LogUtil.error(`${TAG} getUsedAppInMonthList queryAppStatsInfos error, code:${err?.code}, message:${err?.message}`);
}
let map: Map<number, string[]> = new Map<number, string[]>();
Object.keys(res).forEach(key => {
let bundleInfos: usageStatistics.BundleStatsInfo[] = res[key];
bundleInfos.forEach(bundleInfo => {
if (bundleInfo.bundleName && bundleInfo.appIndex !== undefined) {
let bundleList: string[] = map.get(bundleInfo.appIndex) ?? [];
bundleList.push(bundleInfo.bundleName);
map.set(bundleInfo.appIndex, bundleList);
}
})
})
LogUtil.info(`${TAG} getUsedAppInMonthListMap finish`);
return map;
}
/**
* 获取应用锁应用
*
* @returns 应用锁应用列表
*/
public static async getLockedApps(): Promise<Map<number, string[]>> {
LogUtil.info(`${TAG} getLockedApps start`);
let map: Map<number, string[]> = new Map<number, string[]>();
if (DeviceUtil.isDevicePc()) {
LogUtil.info(`${TAG} pc not support appLock`);
return map;
}
try {
let userID: number = await AppUtils.getActivatedAccountLocalId();
// let appInfos: appLock.AppInfo[] = await appLock.getSwitchEnabledAppInfos(userID);
// appInfos.forEach(appInfo => {
// let bundleList: string[] = map.get(appInfo.appIndex) ?? [];
// bundleList.push(appInfo.bundleName);
// map.set(appInfo.appIndex, bundleList);
// });
} catch (err) {
LogUtil.error(`${TAG} getLockedApps error, message: ${err?.message}, code: ${err?.code}`);
}
LogUtil.info(`${TAG} getLockedApps finish`);
return map;
}
/**
* 获取应用列表(不区分分身应用/主应用)
*
* @param appMap 应用列表
* @returns 应用列表(不区分分身应用/主应用)
*/
private static getAllAppListFromMapIgnoreAppIndex(appMap: Map<number, string[]>): string[] {
let appList: string[] = [];
appMap.forEach((appInfos: string[], key: number) => {
appInfos.forEach(appInfo => {
appList.push(appInfo);
})
})
return appList;
}
/**
* 获取不常用应用列表
*
* @returns 不常用应用列表
*/
static async getUnusedBundleInfoList(): Promise<bundleManager.BundleInfo[]> {
LogUtil.info(`${TAG} getUnUsedBundleInfoList start`);
let lockedAppInfoMap: Map<number, string[]> = new Map();
let usedAppMap: Map<number, string[]> = new Map();
let bundleInfos: bundleManager.BundleInfo[] = [];
let allLauncherBundleLabelList: AppEntry[] = [];
let group: taskpool.TaskGroup = new taskpool.TaskGroup();
group.addTask(getLockedAppsAsync);
group.addTask(getUsedAppInMonthMapAsync);
group.addTask(getAllBundleInfoAsync);
group.addTask(getLauncherBundleListAsync);
let res: Object[] = [];
try {
res = await taskpool.execute(group, taskpool.Priority.HIGH);
} catch (err) {
LogUtil.error(`${TAG} getUnusedBundleInfoList taskpool err, code: ${err?.code}, message: ${err?.message}`);
}
if (!CheckEmptyUtils.isEmptyArr(res) && res.length === 4) {
lockedAppInfoMap = res[0] as Map<number, string[]>;
usedAppMap = res[1] as Map<number, string[]>;
bundleInfos = res[2] as bundleManager.BundleInfo[];
allLauncherBundleLabelList = res[3] as AppEntry[];
}
let usedAppsIgnoreAppIndex: string[] = StorageUtil.getAllAppListFromMapIgnoreAppIndex(usedAppMap);
let lockedAppsIgnoreAppIndex: string[] = StorageUtil.getAllAppListFromMapIgnoreAppIndex(lockedAppInfoMap);
let currentTime = new Date().getTime();
// 过滤可卸载、应用锁、常用应用
bundleInfos = bundleInfos.filter(bundleInfo => {
if (!bundleInfo || !bundleInfo.appInfo) {
return false;
}
// 过滤可卸载应用
if (!bundleInfo.appInfo.removable) {
return false;
}
// 过滤系统应用
if (bundleInfo.appInfo?.systemApp) {
return false;
}
// 过滤30天内安装应用
let lastInstallTime = bundleInfo.updateTime;
if (lastInstallTime <= 0) {
LogUtil.info(`${TAG} bad installTime:${lastInstallTime},name:${bundleInfo.name},index:${bundleInfo.appIndex}`);
return false;
}
if (currentTime - lastInstallTime < LONGEST_UNUSED_TIME) {
return false;
}
let bundleName: string = bundleInfo.name;
// 主应用需要过滤分身应用
let index: number = bundleInfo.appIndex;
if (index === MAIN_APP_INDEX) {
if (lockedAppsIgnoreAppIndex.includes(bundleName)) {
return false;
}
if (usedAppsIgnoreAppIndex.includes(bundleName)) {
return false;
}
} else {
// 过滤应用锁应用
let lockedList = lockedAppInfoMap.get(index);
if (lockedList && lockedList.includes(bundleInfo.name)) {
return false;
}
// 过滤常用应用
let usedAppList = usedAppMap.get(index);
if (usedAppList && usedAppList.includes(bundleInfo.name)) {
return false;
}
}
// 只显示桌面应用
for (let i = 0; i < allLauncherBundleLabelList.length; i++) {
let appInfo: AppEntry = allLauncherBundleLabelList[i];
if (appInfo.name === bundleInfo.name && appInfo.appIndex === bundleInfo.appIndex) {
return true;
}
}
return false;
});
LogUtil.info(`${TAG} getUnUsedBundleInfoList finish ${bundleInfos.length}`);
return bundleInfos;
}
/**
* 获取全量应用列表
*
* @returns 全量应用列表
*/
public static async getAllBundleInfo(): Promise<bundleManager.BundleInfo[]> {
// 获取全量应用列表
let bundleInfos: bundleManager.BundleInfo[] = [];
try {
bundleInfos = await bundleManager.getAllBundleInfo(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);
} catch (err) {
LogUtil.error(`${TAG} getAllBundleInfo error, code:${err?.code}, message:${err?.message}`);
}
return bundleInfos;
}
/**
* 获取存储二级界面显示应用列表,参考AppListLoader.refreshAppList
*
* @returns 存储二级界面显示应用列表
*/
public static async getLauncherBundleList(): Promise<AppEntry[]> {
let userID: number = await AppUtils.getActivatedAccountLocalId();
let allLauncherBundleLabelList: launcherBundleResource.LauncherAbilityInfo[] = [];
try {
allLauncherBundleLabelList = await launcherBundleResource.getAllLauncherAbilityInfo(userID);
} catch (err) {
LogUtil.error(`${TAG} getAllLauncherAbilityInfo error, code:${err?.code}, message:${err?.message}`);
return [];
}
let appList: AppEntry[] = [];
allLauncherBundleLabelList.forEach(appInfo => {
let appEntry = new AppEntry({
name: appInfo.applicationInfo?.name,
versionName: '',
updateTime: 0,
appInfo: {
name: appInfo.applicationInfo?.name,
removable: appInfo.applicationInfo?.removable,
appIndex: appInfo.applicationInfo?.appIndex,
}
} as bundleManager.BundleInfo);
appList.push(appEntry);
})
return AppListLoader.getInstance().filterAppList(appList);
}
/**
* 获取已使用存储空间的比例,如: 已使用50%,返回50
*
* @param freeSize 空闲空间大小,单位为byte
*
* @returns 已使用的存储空间比例
*/
public static async getUsedProportion(freeSize?: number): Promise<number> {
let detail: storage.StorageStats | undefined = await StorageUtil.getStorageDetail();
let freeBytesNumber: number = freeSize ?? await StorageUtil.getStorageFreeSize();
if (detail && detail.total !== 0 && freeBytesNumber >= 0) {
let usedBytesNumber = detail.total - freeBytesNumber;
let usedProportion = 0;
let percent = usedBytesNumber / detail.total * 100;
if (percent < 1) {
usedProportion = Math.ceil(Number(percent)).valueOf();
} else {
usedProportion = Math.floor(Number(percent)).valueOf();
}
LogUtil.info(`${TAG} usedProportion : ${usedProportion}`);
return usedProportion;
}
return 0;
}
/**
* 获取当前存储剩余空间大小
*
* @returns 当前存储剩余空间大小
*/
public static async getStorageFreeSize(): Promise<number> {
let freeBytesNumber: number = INVALID_DATA_SIZE;
try {
freeBytesNumber = await storage.getFreeSize();
} catch (err) {
LogUtil.error(`${TAG} getFreeSize error, code: ${err?.code}, message: ${err?.message}`);
}
return freeBytesNumber;
}
/**
* 获取当前存储状态信息
*
* @returns 当前存储状态信息
*/
public static async getStorageDetail(): Promise<storage.StorageStats | undefined> {
let detail: storage.StorageStats | undefined = undefined;
try {
return await storage.getUserStorageStats();
} catch (err) {
LogUtil.error(`${TAG} getUserStorageStats error, code: ${err?.code}, message: ${err?.message}`);
}
return detail;
}
/**
* 获取应用的存储统计数据
*
* @param bundleName 应用包名
* @param appIndex 应用索引 默认为0
* @param statFlag 统计数据标志
* @returns 应用的存储统计数据
*/
public static async getBundleStats(bundleName: string, appIndex?: number,
statFlag?: NoGetBundleStatsFlag): Promise<storageStatistics.BundleStats | undefined> {
let res: storageStatistics.BundleStats | undefined;
res = await StorageUtil.getStatisticsBundleStats(bundleName, appIndex, statFlag);
return res;
}
private static async getStatisticsBundleStats(bundleName: string, appIndex?: number,
statFlag?: NoGetBundleStatsFlag): Promise<storageStatistics.BundleStats | undefined> {
const index: number = appIndex ?? 0;
if (CheckEmptyUtils.checkStrIsEmpty(bundleName) || index < 0) {
return undefined;
}
try {
const flag = statFlag ?? NoGetBundleStatsFlag.GET_BUNDLE_WITH_ALL_SIZE;
// return await storageStatistics.getBundleStats(bundleName, index, flag);
return await storageStatistics.getBundleStats(bundleName, index);
} catch (error) {
LogUtil.error(`${TAG} getStatisticsBundleStats failed, ${bundleName}, error: ${error?.code}, ${error?.message}`);
}
return undefined;
}
}