Android 设置存储修改
源码8.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);
}
svp.getPrimaryStorageSize() 等于获取/data的总大小 等于 fsstat.f_blocks * fsstat.f_frsize
// frameworks/base/core/java/android/os/storage/StorageManager.java
/** {@hide} */
public long getPrimaryStorageSize() {
return FileUtils.roundStorageSize(Environment.getDataDirectory().getTotalSpace());
}
public long getTotalSpace() {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("getFileSystemAttributes"));
sm.checkRead(path);
}
if (isInvalid()) {
return 0L;
}
return fs.getSpace(this, FileSystem.SPACE_TOTAL);
}
/**
* 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.
*/
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;
}
// libcore/ojluni/src/main/native/UnixFileSystem_md.c
// Android-changed: Name changed because of added thread policy check
JNIEXPORT jlong JNICALL
Java_java_io_UnixFileSystem_getSpace0(JNIEnv *env, jobject this,
jobject file, jint t)
{
jlong rv = 0L;
WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
struct statvfs64 fsstat;
memset(&fsstat, 0, sizeof(fsstat));
if (statvfs64(path, &fsstat) == 0) {
switch(t) {
case java_io_FileSystem_SPACE_TOTAL:
rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
long_to_jlong(fsstat.f_blocks));
break;
case java_io_FileSystem_SPACE_FREE:
rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
long_to_jlong(fsstat.f_bfree));
break;
case java_io_FileSystem_SPACE_USABLE:
rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
long_to_jlong(fsstat.f_bavail));
break;
default:
assert(0);
}
}
} END_PLATFORM_STRING(env, path);
return rv;
}
但是 FileUtils.roundStorageSize
函数会四舍五入成2的幂次, 下面是小米手机的226,但实际设置中显示的是256
volume.getPath().getFreeSpace() 通过 dumpsys mount
也可以获取 path=/storage/emulated
# dumpsys mount
Disks:
Volumes:
VolumeInfo{emulated}:
type=EMULATED diskId=null partGuid=null mountFlags=PRIMARY|VISIBLE mountUserId=-1 state=MOUNTED
fsType=null fsUuid=null fsLabel=null
path=/storage/emulated internalPath=/data/media
还有一个加载数据的地方在StorageDashboardFragment.onReceivedSizes()函数中,最终调用的是VolumeSizesLoader.getVolumeSize()
// packages/apps/Settings/src/com/android/settings/deviceinfo/storage/VolumeSizesLoader.java
@VisibleForTesting
static PrivateStorageInfo getVolumeSize(
StorageVolumeProvider storageVolumeProvider, StorageStatsManager stats, VolumeInfo info)
throws IOException {
// 最终实现在StorageStatsService中
long privateTotalBytes = storageVolumeProvider.getTotalBytes(stats, info);
long privateFreeBytes = storageVolumeProvider.getFreeBytes(stats, info);
return new PrivateStorageInfo(privateFreeBytes, privateTotalBytes);
}
最终实现在StorageStatsService中
//frameworks/base/services/usage/java/com/android/server/usage/StorageStatsService.java
@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) {
...
if (isQuotaSupported(volumeUuid, callingPackage)) {
final long cacheTotal = getCacheBytes(volumeUuid, callingPackage);
final long cacheReserved = mStorage.getStorageCacheBytes(path, 0);
final long cacheClearable = Math.max(0, cacheTotal - cacheReserved);
return path.getUsableSpace() + cacheClearable;
} else {
return path.getUsableSpace();
}
...
}
总结
- 设置中的存储信息最终是通过StorageStatsService中的
getFreeBytes
和getTotalBytes
获取 getTotalBytes
返回的是 mStorage.getPrimaryStorageSize(), 实际是statvfs("/data")
的fsstat.f_blocks * fsstat.f_frsize
getFreeBytes
返回的是path.getUsableSpace()
实际是statvfs("/data")
的fsstat.f_frsize * fsstat.f_bavail
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)