Android10-系统存储空间显示源码分析
1、系统设置显示
包括总空间,已用空间,可用空间
源码位置:packages/apps/Settings/src/com/android/settings/deviceinfo/storage/StorageSummaryDonutPreferenceController.java
/**
* Updates the state of the donut preference for the next update using volume to summarize.
*
* @param volume VolumeInfo to use to populate the informayion.
*/
public void updateSizes(StorageVolumeProvider svp, VolumeInfo volume) {
final long sharedDataSize = volume.getPath().getTotalSpace();
long totalSize = svp.getPrimaryStorageSize();
if (totalSize <= 0) {
totalSize = sharedDataSize;
}
//已用空间=总空间-可用空间
final long usedBytes = totalSize - volume.getPath().getFreeSpace();
updateBytes(usedBytes, totalSize);
}
2.StorageVolumeProvider
源码位置frameworks/base/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java
它是一个接口,实现类为frameworks/base/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java
/**
* StorageVolumeProvider provides access to the storage volumes on a device for free space
* calculations.
StorageVolumeProvider 提供设备上各个分卷可用空间的计算方法
*/
public interface StorageVolumeProvider {
/**
* Returns the number of bytes of total storage on the primary storage.
计算主分区的总大小
*/
long getPrimaryStorageSize();
/**
* Returns a list of VolumeInfos for the device.
返回分卷列表
*/
List<VolumeInfo> getVolumes();
/**
* Returns the emulated volume for a given private volume.
返回指定私有卷对应的模拟卷
*/
VolumeInfo findEmulatedForPrivate(VolumeInfo privateVolume);
/**
* Returns the total bytes for a given storage volume.
*返回指定分卷的大小
* @pre The volume is a private volume and is readable.
*/
long getTotalBytes(StorageStatsManager stats, VolumeInfo volume) throws IOException;
/**
* Returns the free bytes for a given storage volume.
*返回指定分卷的可用空间大小
* @pre The volume is a private volume and is readable.
*/
long getFreeBytes(StorageStatsManager stats, VolumeInfo volume) throws IOException;
}
3.StorageManagerVolumeProvider
源码位置frameworks/base/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java
public class StorageManagerVolumeProvider implements StorageVolumeProvider {
private StorageManager mStorageManager;
//传入StorageManager作为参数,它是重点
public StorageManagerVolumeProvider(StorageManager sm) {
mStorageManager = sm;
}
@Override
public long getPrimaryStorageSize() {
return mStorageManager.getPrimaryStorageSize();
}
@Override
public List<VolumeInfo> getVolumes() {
return mStorageManager.getVolumes();
}
@Override
public VolumeInfo findEmulatedForPrivate(VolumeInfo privateVolume) {
return mStorageManager.findEmulatedForPrivate(privateVolume);
}
@Override
public long getTotalBytes(StorageStatsManager stats, VolumeInfo volume) throws IOException {
return stats.getTotalBytes(volume.getFsUuid());
}
@Override
public long getFreeBytes(StorageStatsManager stats, VolumeInfo volume) throws IOException {
return stats.getFreeBytes(volume.getFsUuid());
}
}
4.重点来了StorageManager
源码位置frameworks/base/core/java/android/os/storage/StorageManager.java
先分析下面三个方法,另外两个后面分析
private final IStorageManager mStorageManager;
//获取总大小
public long getPrimaryStorageSize() {
//总大小是用户空间+系统空间,并进行二次方化
return FileUtils.roundStorageSize(Environment.getDataDirectory().getTotalSpace()
+ Environment.getRootDirectory().getTotalSpace());
}
//获取所有卷标
public @NonNull List<VolumeInfo> getVolumes() {
try {
return Arrays.asList(mStorageManager.getVolumes(0));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
//查找私有卷的模拟卷
public @Nullable VolumeInfo findEmulatedForPrivate(VolumeInfo privateVol) {
if (privateVol != null) {
return findVolumeById(privateVol.getId().replace("private", "emulated"));
} else {
return null;
}
}
//根据卷ID查找卷
public @Nullable VolumeInfo findVolumeById(String id) {
Preconditions.checkNotNull(id);
// TODO; go directly to service to make this faster
for (VolumeInfo vol : getVolumes()) {
if (Objects.equals(vol.id, id)) {
return vol;
}
}
return null;
}
5.getPrimaryStorageSize方法
源码位置:frameworks/base/core/java/android/os/FileUtils.java
/**
* Round the given size of a storage device to a nice round power-of-two
* value, such as 256MB or 32GB. This avoids showing weird values like
* "29.5GB" in UI.
将存储设备的给定大小四舍五入为漂亮的 2 次方
值,例如 256MB 或 32GB。这样可以避免显示奇怪的值,例如
用户界面中的“29.5GB”。
*
* @hide
*/
public static long roundStorageSize(long size) {
long val = 1;
long pow = 1;
while ((val * pow) < size) {
val <<= 1;
if (val > 512) {
val = 1;
pow *= 1000;
}
}
return val * pow;
}
frameworks/base/core/java/android/os/Environment.java
private static final String ENV_ANDROID_ROOT = "ANDROID_ROOT";
private static final String ENV_ANDROID_DATA = "ANDROID_DATA";
public static final String DIR_ANDROID = "Android";
private static final String DIR_DATA = "data";
//系统分区对应根目录/system
private static final File DIR_ANDROID_ROOT = getDirectory(ENV_ANDROID_ROOT, "/system");
//用户分区对应根目录/data
private static final File DIR_ANDROID_DATA = getDirectory(ENV_ANDROID_DATA, "/data");
static File getDirectory(String variableName, String defaultPath) {
String path = System.getenv(variableName); //获取是否有定义属性,见下面定义,一般是没有的
return path == null ? new File(defaultPath) : new File(path);
}
/**
* Return the user data directory.
返回用户空间
*/
public static File getDataDirectory() {
return DIR_ANDROID_DATA;
}
/**
* Return root of the "system" partition holding the core Android OS.
* Always present and mounted read-only.
返回支行Android OS的system分区,该分区始终以只读的方式存在和挂载
*/
public static @NonNull File getRootDirectory() {
return DIR_ANDROID_ROOT;
}
java.lang.System
/**
* Returns the value of the environment variable with the given name, or null if no such
* variable exists.
*/
public static String getenv(String name) {
if (name == null) {
throw new NullPointerException("name == null");
}
return Libcore.os.getenv(name);
}
/data分区和/system分区配置
源码位置:device/sprd/sharkle/sl8541e_1h10_32b/sl8541e_1h10_32b.xml
<Partitions>
<!-- size unit is MBytes -->
<Partition id="prodnv" size="10"/>
<Partition id="miscdata" size="1"/>
<Partition id="recovery" size="35"/>
<Partition id="misc" size="1"/>
<Partition id="trustos" size="6"/>
<Partition id="trustos_bak" size="6"/>
<Partition id="sml" size="1"/>
<Partition id="sml_bak" size="1"/>
<Partition id="uboot" size="1"/>
<Partition id="uboot_bak" size="1"/>
<Partition id="uboot_log" size="4"/>
<Partition id="logo" size="4"/>
<Partition id="fbootlogo" size="4"/>
<Partition id="l_fixnv1" size="1"/>
<Partition id="l_fixnv2" size="1"/>
<Partition id="l_runtimenv1" size="1"/>
<Partition id="l_runtimenv2" size="1"/>
<Partition id="gpsgl" size="1"/>
<Partition id="gpsbd" size="1"/>
<Partition id="wcnmodem" size="10"/>
<Partition id="persist" size="2"/>
<Partition id="l_modem" size="25"/>
<Partition id="l_deltanv" size="1"/>
<Partition id="l_gdsp" size="10"/>
<Partition id="l_ldsp" size="20"/>
<Partition id="pm_sys" size="1"/>
<Partition id="boot" size="35"/>
<Partition id="dtbo" size="8"/>
<Partition id="super" size="3100"/> <!--system product vendor三个镜像+预留空间-->
<Partition id="cache" size="150"/>
<Partition id="socko" size="75"/>
<Partition id="odmko" size="25"/>
<Partition id="vbmeta" size="1"/>
<Partition id="vbmeta_bak" size="1"/>
<Partition id="sysdumpdb" size="10"/>
<Partition id="metadata" size="16"/>
<Partition id="vbmeta_system" size="1"/>
<Partition id="vbmeta_vendor" size="1"/>
<Partition id="userdata" size="0xFFFFFFFF"/>
device/sprd/sharkle/sl8541e_1h10_32b/BoardConfig.mk
sprdiskexist := $(shell if [ -f $(TOPDIR)sprdisk/Makefile -a "$(TARGET_BUILD_VARIANT)" = "userdebug" ]; then echo "exist"; else echo "notexist"; fi;)
ifneq ($(sprdiskexist), exist)
TARGET_NO_SPRDISK := true
else
TARGET_NO_SPRDISK := false
endif
SPRDISK_BUILD_PATH := sprdisk/
BOARD_SUPER_PARTITION_SIZE := 3250585600 #system product vendor三个镜像+预留空间
BOARD_GROUP_UNISOC_SIZE := 3250585600
# ext4 partition layout
#BOARD_VENDORIMAGE_PARTITION_SIZE := 314572800
BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE := ext4
TARGET_COPY_OUT_VENDOR=vendor
TARGET_USERIMAGES_USE_EXT4 := true
BOARD_BOOTIMAGE_PARTITION_SIZE := 36700160
BOARD_RECOVERYIMAGE_PARTITION_SIZE := 36700160
#BOARD_SYSTEMIMAGE_PARTITION_SIZE := 1625292800
BOARD_CACHEIMAGE_PARTITION_SIZE := 150000000
BOARD_PRODNVIMAGE_PARTITION_SIZE := 10485760
BOARD_USERDATAIMAGE_PARTITION_SIZE := 134217728
BOARD_DTBIMG_PARTITION_SIZE := 8388608
BOARD_DTBOIMG_PARTITION_SIZE := 8388608
BOARD_FLASH_BLOCK_SIZE := 4096
BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE := ext4
BOARD_PRODNVIMAGE_FILE_SYSTEM_TYPE := ext4
TARGET_SYSTEMIMAGES_SPARSE_EXT_DISABLED := true
TARGET_USERIMAGES_SPARSE_EXT_DISABLED := false
BOARD_PERSISTIMAGE_PARTITION_SIZE := 2097152
TARGET_PRODNVIMAGES_SPARSE_EXT_DISABLED := true
TARGET_CACHEIMAGES_SPARSE_EXT_DISABLED := false
USE_SPRD_SENSOR_HUB := false
#BOARD_PRODUCTIMAGE_PARTITION_SIZE :=419430400
BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE := ext4
TARGET_COPY_OUT_PRODUCT=product
BOARD_SOCKOIMAGE_FILE_SYSTEM_TYPE := ext4
BOARD_ODMKOIMAGE_FILE_SYSTEM_TYPE := ext4
BOARD_SOCKOIMAGE_PARTITION_SIZE := 78643200 # 75M
BOARD_ODMKOIMAGE_PARTITION_SIZE := 26214400 # 25M
#creates the metadata directory
BOARD_USES_METADATA_PARTITION := true
总结:修改除 system、cache、prodnv、data 之外的分区只需要修改工具中 project.xml 文件即可,修
改好 xml 文件之后请重新制作 pac 包,以确保修改成功
6.getVolumes方法
从5中看到,调用了AIDL接口,源码位置frameworks/base/services/core/java/com/android/server/StorageManagerService.java
/** Map from volume ID to disk */
//该结构体保存卷信息<VolumeInfo.id,VolumeInfo>
private final ArrayMap<String, VolumeInfo> mVolumes = new ArrayMap<>();
//返回所有卷标
@Override
public VolumeInfo[] getVolumes(int flags) {
synchronized (mLock) {
final VolumeInfo[] res = new VolumeInfo[mVolumes.size()];
for (int i = 0; i < mVolumes.size(); i++) {
res[i] = mVolumes.valueAt(i);
}
return res;
}
}
frameworks/base/core/java/android/os/storage/VolumeInfo.java
/** Stub volume representing internal private storage */
public static final String ID_PRIVATE_INTERNAL = "private";
/** Real volume representing internal emulated storage */
public static final String ID_EMULATED_INTERNAL = "emulated";
//类型,PUBLIC,PRIVATE ASEC,OBB,STUB
public static final int TYPE_PUBLIC = IVold.VOLUME_TYPE_PUBLIC;
public static final int TYPE_PRIVATE = IVold.VOLUME_TYPE_PRIVATE;
public static final int TYPE_EMULATED = IVold.VOLUME_TYPE_EMULATED;
public static final int TYPE_ASEC = IVold.VOLUME_TYPE_ASEC;
public static final int TYPE_OBB = IVold.VOLUME_TYPE_OBB;
public static final int TYPE_STUB = IVold.VOLUME_TYPE_STUB;
//主要有以下成员变量
public final String id; //id
public final int type; //权限类型
public final DiskInfo disk; //磁盘信息
public final String partGuid; //分区组ID
public int mountFlags = 0; //挂载标志位
public int mountUserId = UserHandle.USER_NULL; //用户
public int state = STATE_UNMOUNTED; //挂载状态
public String fsType; //文件系统类型
public String fsUuid; //文件系统id
public String fsLabel;//文件系统标签
public String path; //路径
public String internalPath;//内部内径
/* SPRD: add for storage manage */
public String linkName;
/* @SPRD: add for UMS */
public int stateBeforeUMS = STATE_UNMOUNTED;
7.接第3节,另外两个没有分析的方法
源码位置:frameworks/base/core/java/android/app/usage/StorageStatsManager.java
private final IStorageStatsManager mService;
//获取指定文件系统的大小 stats.getTotalBytes(volume.getFsUuid());
public @BytesLong long getTotalBytes(@NonNull UUID storageUuid) throws IOException {
try {
return mService.getTotalBytes(convert(storageUuid), mContext.getOpPackageName());
} catch (ParcelableException e) {
e.maybeRethrow(IOException.class);
throw new RuntimeException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
//获取指定文件系统的可用空间大小 stats.getFreeBytes(volume.getFsUuid());
public @BytesLong long getFreeBytes(@NonNull UUID storageUuid) throws IOException {
try {
return mService.getFreeBytes(convert(storageUuid), mContext.getOpPackageName());
} catch (ParcelableException e) {
e.maybeRethrow(IOException.class);
throw new RuntimeException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
frameworks/base/services/usage/java/com/android/server/usage/StorageStatsService.java
private final StorageManager mStorage;
@Override
public long getTotalBytes(String volumeUuid, String callingPackage) {
// NOTE: No permissions required
if (volumeUuid == StorageManager.UUID_PRIVATE_INTERNAL) {
return FileUtils.roundStorageSize(mStorage.getPrimaryStorageSize());
} else {
final VolumeInfo vol = mStorage.findVolumeByUuid(volumeUuid);
if (vol == null) {
throw new ParcelableException(
new IOException("Failed to find storage device for UUID " + volumeUuid));
}
return FileUtils.roundStorageSize(vol.disk.size);
}
}
@Override
public long getFreeBytes(String volumeUuid, String callingPackage) {
// NOTE: No permissions required
final long token = Binder.clearCallingIdentity();
try {
final File path;
try {
path = mStorage.findPathForUuid(volumeUuid);
} catch (FileNotFoundException e) {
throw new ParcelableException(e);
}
// Free space is usable bytes plus any cached data that we're
// willing to automatically clear. To avoid user confusion, this
// logic should be kept in sync with getAllocatableBytes().
if (isQuotaSupported(volumeUuid, PLATFORM_PACKAGE_NAME)) {
final long cacheTotal = getCacheBytes(volumeUuid, PLATFORM_PACKAGE_NAME);
final long cacheReserved = mStorage.getStorageCacheBytes(path, 0);
final long cacheClearable = Math.max(0, cacheTotal - cacheReserved);
return path.getUsableSpace() + cacheClearable;
} else {
return path.getUsableSpace();
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
实际上还是调用的StorageManager进行操作,StorageManager又调用到了StorageManagerService,与前面分析流程基本一致。