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();
            }
        ...

    }

总结

  1. 设置中的存储信息最终是通过StorageStatsService中的 getFreeBytesgetTotalBytes 获取
  2. getTotalBytes返回的是 mStorage.getPrimaryStorageSize(), 实际是 statvfs("/data")fsstat.f_blocks * fsstat.f_frsize
  3. getFreeBytes 返回的是 path.getUsableSpace() 实际是statvfs("/data")fsstat.f_frsize * fsstat.f_bavail
posted @ 2022-12-20 17:34  梦过无声  阅读(252)  评论(0编辑  收藏  举报