当前位置: 首页 > article >正文

Android9~Android13 某些容量SD卡被格式化为内部存储时容量显示错误问题的研究与解决方案

声明:原创文章,禁止转载!

Android9~Android13 某些容量SD卡被格式化为内部存储时容量显示错误问题的研究与解决方案

分析Android11 系统对于EMMC/UFS作为内部存储、SD卡被格式化为内部存储、SD卡/U盘被格式化为便携式存储的不同处理


一.现象描述

实测Android9 Android10 Android11 Android12 Android13系统中某些容量的SD卡在被格式化为内部存储时,在设置中的显示容量与实际容量不符,比如某些16GB容量的SD卡在设置->存储中显示为32GB,但是如果选择“格式化为便携式存储设备”的话可以正常显示容量为16GB。

在Android11系统格式化为内部存储设备和便携式存储设备

同一个SD卡在Android7系统上作为内部存储和便携式存储空间时显示如下


二.源码分析

Android11系统

packages/apps/Settings/src/com/android/settings/deviceinfo/StorageSettings.java

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);

        final Context context = getActivity();

        mStorageManager = context.getSystemService(StorageManager.class);

        if (sTotalInternalStorage <= 0) {
            sTotalInternalStorage = mStorageManager.getPrimaryStorageSize();
        }

        addPreferencesFromResource(R.xml.device_info_storage);

        mInternalCategory = (PreferenceCategory) findPreference("storage_internal");
        mExternalCategory = (PreferenceCategory) findPreference("storage_external");

        mInternalSummary = new StorageSummaryPreference(getPrefContext());

        setHasOptionsMenu(true);
    }
	
    private synchronized void refresh() {
        final Context context = getPrefContext();

        getPreferenceScreen().removeAll();
        mInternalCategory.removeAll();
        mExternalCategory.removeAll();

        mInternalCategory.addPreference(mInternalSummary);

        final StorageManagerVolumeProvider smvp = new StorageManagerVolumeProvider(mStorageManager);
        final PrivateStorageInfo info = PrivateStorageInfo.getPrivateStorageInfo(smvp);
        final long privateTotalBytes = info.totalBytes;
        final long privateUsedBytes = info.totalBytes - info.freeBytes;

        final List<VolumeInfo> volumes = mStorageManager.getVolumes();
        Collections.sort(volumes, VolumeInfo.getDescriptionComparator());

        for (VolumeInfo vol : volumes) {
            if (vol.getType() == VolumeInfo.TYPE_PRIVATE) {

                if (vol.getState() == VolumeInfo.STATE_UNMOUNTABLE) {
                    mInternalCategory.addPreference(
                            new StorageVolumePreference(context, vol, 0));
                } else {
                    final long volumeTotalBytes = PrivateStorageInfo.getTotalSize(vol,
                            sTotalInternalStorage);
                    mInternalCategory.addPreference(
                            new StorageVolumePreference(context, vol, volumeTotalBytes));
                }
            } else if (vol.getType() == VolumeInfo.TYPE_PUBLIC
                    || vol.getType() == VolumeInfo.TYPE_STUB) {
                mExternalCategory.addPreference(
                        new StorageVolumePreference(context, vol, 0));
            }
        }

packages/apps/Settings/src/com/android/settings/deviceinfo/StorageVolumePreference.java

    public StorageVolumePreference(Context context, VolumeInfo volume, long totalBytes) {
        super(context);

        mStorageManager = context.getSystemService(StorageManager.class);
        mVolume = volume;

        if (volume.isMountedReadable()) {
            // TODO: move statfs() to background thread
            final File path = volume.getPath();

            long freeBytes = 0;
            long usedBytes = 0;
            if (volume.getType() == VolumeInfo.TYPE_PRIVATE) {
                final StorageStatsManager stats =
                        context.getSystemService(StorageStatsManager.class);
                try {
					//作为TYPE_PRIVATE,调用StorageStatsManager.getTotalBytes接口获取存储总容量大小
                    totalBytes = stats.getTotalBytes(volume.getFsUuid());
					//作为TYPE_PRIVATE,调用StorageStatsManager.getFreeBytes接口获取存储可用容量大小
                    freeBytes = stats.getFreeBytes(volume.getFsUuid());
                    usedBytes = totalBytes - freeBytes;
                } catch (IOException e) {
                    Log.w(TAG, e);
                }
            } else {
                // StorageStatsManager can only query private volumes.
                // Default to previous storage calculation for public volumes.
                if (totalBytes <= 0) {
					/*
					作为便携式存储,调用File.getTotalSpace接口获取存储总容量大小。
					注意此处并没有调用FileUtils.roundStorageSize接口进行向上整数对齐,
					那么为什么这个SD卡被格式化为便携式存储设备后在设置中显示的是"16GB"整数呢,
					下面会有详细解答
					*/
                    totalBytes = path.getTotalSpace();
                }
                freeBytes = path.getFreeSpace();//作为便携式存储,调用File.getFreeSpace接口获取存储可用容量大小
                usedBytes = totalBytes - freeBytes;
            }

            final String used = Formatter.formatFileSize(context, usedBytes);
            final String total = Formatter.formatFileSize(context, totalBytes);
            setSummary(context.getString(R.string.storage_volume_summary, used, total));
            if (totalBytes > 0) {
                mUsedPercent = (int) ((usedBytes * 100) / totalBytes);
            }

frameworks/base/core/java/android/app/usage/StorageStatsManager.java

    private final IStorageStatsManager mService;

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

    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/core/java/android/app/usage/IStorageStatsManager.aidl

interface IStorageStatsManager {
    boolean isQuotaSupported(String volumeU

http://www.kler.cn/news/232848.html

相关文章:

  • 贪心算法入门题(算法村第十七关青铜挑战)
  • Get Ready!这些 ALVA 应用即将上线 Vision Pro!
  • C语言:分支与循环
  • nodejs+vue高校实验室耗材管理系统_m20vy
  • 探索XGBoost:参数调优与模型解释
  • 【网工】华为设备命令学习(服务器发布)
  • 程序设计语言之机器语言、汇编语言、高级语言
  • 【制作100个unity游戏之24】unity制作一个3D动物AI生态系统游戏3(附项目源码)
  • 《Docker极简教程》--Docker环境的搭建-在Windows上搭建Docker环境
  • Elasticsearch 安装和配置脚本文档
  • UE4运用C++和框架开发坦克大战教程笔记(十九)(第58~60集)完结
  • 通俗易懂:快速排序算法全解析
  • TCP/IP协议以及UDP(超详细,看这一篇就够了)
  • Docker配置Portainer容器管理界面
  • StarRocks 1 月社区动态(2024)
  • Android AOSP源码研究之万事开头难----经验教训记录
  • 强化学习 | 基于 Q-Learning 算法解决 Treasure on Right 游戏
  • 分享90个行业PPT,总有一款适合您
  • Linux 命令行的世界 :2.文件系统中跳转
  • Transformer的PyTorch实现之若干问题探讨(二)
  • Linux的可移植性
  • 【FFmpeg】ffplay 命令行参数 ⑤ ( 设置音频滤镜 -af 参数 | 设置统计信息 -stats 参数 | 设置同步时钟类型 -sync 参数 )
  • 第60讲公共Tabs组件封装
  • MQTT 服务器(emqx)搭建及使用
  • 机器人搬砖 - 华为OD统一考试
  • C++服务器端开发(7):并发处理
  • FolkMQ “单线程“消息中间件(开源) v1.0.32 发布
  • 【笔记】Helm-5 Chart模板指南-8 命名模板
  • 排序算法---堆排序
  • CTF--Web安全--SQL注入之‘绕过方法’