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,与前面分析流程基本一致。