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

news2024/11/17 14:27:27

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

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.coloradmin.cn/o/1435588.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++:深入剖析默认参数

看下列代码执行结果&#xff0c;你猜一猜会输出什么&#xff1f; #include<iostream> using namespace std; struct A {virtual void fun(int a 10) {cout << "A,a"<<a;} }; struct B :public A {void fun(int a 5) {cout <<"B,a&qu…

极狐GitLab 使用阿里云作为 OmniAuth 身份验证 provider

使用阿里云作为 OmniAuth 身份验证 provider 您可以启用阿里云 OAuth 2.0 OmniAuth provider并使用您的阿里云账户登录极狐GitLab。 创建阿里云应用 登录阿里云平台&#xff0c;在上面创建一个应用。阿里云会生成一个 client ID and secret key 供您使用。 登录到阿里云平台…

我的世界Java版服务器如何搭建并实现与好友远程联机Minecarft教程

文章目录 1. 安装JAVA2. MCSManager安装3.局域网访问MCSM4.创建我的世界服务器5.局域网联机测试6.安装cpolar内网穿透7. 配置公网访问地址8.远程联机测试9. 配置固定远程联机端口地址9.1 保留一个固定tcp地址9.2 配置固定公网TCP地址9.3 使用固定公网地址远程联机 本教程主要介…

多模态对比语言图像预训练CLIP:打破语言与视觉的界限,具备零样本能力

多模态对比语言图像预训练CLIP:打破语言与视觉的界限,具备零样本能力。 一种基于多模态(图像、文本)对比训练的神经网络。它可以在给定图像的情况下,使用自然语言来预测最相关的文本片段,而无需为特定任务进行优化。CLIP的设计类似于GPT-2和GPT-3,具备出色的零射击能力…

【LeetCode: 73. 矩阵置零 + 矩阵】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

嵌入式软件bug分析基本要求

摘要&#xff1a;软件从来不是一次就能完美的&#xff0c;需要以包容的眼光看待它的残缺。那问题究竟为何产生&#xff0c;如何去除呢&#xff1f; 1、软件问题从哪来 软件缺陷问题千千万万&#xff0c;主要是需求、实现、和运行环境三方面。 1.1 需求描述偏差 客户角度的描…

数据结构高级算法

目录 最小生成树 Kruskal(克鲁斯卡尔)(以边为核心) 9) 不相交集合(并查集合) 基础 Union By Size 图-相关题目 4.2 Greedy Algorithm 1) 贪心例子 Dijkstra Prim Kruskal 最优解(零钱兑换)- 穷举法 Leetcode 322 最优解(零钱兑换)- 贪心法 Leetcode 322 3)…

XUbuntu22.04之两款实用画笔工具(二百一十)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

AR特效自研AI算法技术解决方案

在当今这个高速发展的数字化时代&#xff0c;增强现实&#xff08;AR&#xff09;技术已经成为企业创新和市场竞争的重要手段。美摄科技凭借对AI技术的深厚积累&#xff0c;为企业提供了一套创新的AR特效自研AI算法技术解决方案&#xff0c;旨在满足企业在AR领域的多元化需求。…

运行vue3项目出现的问题

Mac 系统运行 vue 启动项目时报错: Permission denied 的解决方式 控制台运行 chmod 777 node_modules/.bin/vue-cli-service 如果 npm run dev 还报这个错 控制台运行 node node_modules/esbuild/install.js

【工具使用】arm-gcc工具链安装

一&#xff0c;简介 本文介绍如何在linux环境安装arm相关工具链。供参考 二&#xff0c;操作步骤 在linux环境下&#xff0c;解压压缩包&#xff1a; tar -jxvf gcc-arm-none-eabi-9-2019-q4-major-x86_64-linux.tar.bz2.tar.bz2.tar.bz2解压成功&#xff0c;出现文件夹&a…

CSS:两列布局

两列布局是指一列宽度固定&#xff0c;另一列自适应。效果如下&#xff1a; HTML: <div class"container clearfix"><div class"left"></div><div class"right"></div> </div>公共 CSS&#xff1a; .con…

openstack(T版)公有云--Dashboard服务

公有云上OpenStack Train最小化安装_openstack最小化部署-CSDN博客 我的opensatck(T)是参考上面链接去部署完成的&#xff0c;在部署完Dashboard服务后&#xff0c;将要用浏览器访问的时候出现了404 500 Internal Server Error 等各种各样的问题&#xff0c;以下是我排查问题…

Leetcode—57. 插入区间【中等】

2024每日刷题&#xff08;113&#xff09; Leetcode—57. 插入区间 实现代码 class Solution { public:vector<vector<int>> insert(vector<vector<int>>& intervals, vector<int>& newInterval) {vector<vector<int>> an…

thinkphp6入门(17)-- 网站开发中session、cache、cookie的区别

Session&#xff08;会话&#xff09;: 定义&#xff1a; Session是一种用于在服务器端存储用户信息的机制&#xff0c;以跟踪用户的状态。 数据存储位置&#xff1a; 存储在服务器端&#xff0c;可以存在于内存、数据库或文件系统中。 生命周期&#xff1a; 存在于用户访问应…

R语言学习case9:ggplot基础画图(Scatter Metrics 矩阵散点图)

step1: 导入ggplot2库文件 library(ggplot2)step2&#xff1a;带入自带的iris数据集 iris <- datasets::irisstep3&#xff1a;查看数据信息 dim(iris)维度为 [150,5] head(iris)查看数据前6行的信息 step4&#xff1a;利用ggplot工具包绘图 开发者们在ggplot2的基础…

机器学习聚类算法

聚类算法是一种无监督学习方法&#xff0c;用于将数据集中的样本划分为多个簇&#xff0c;使得同一簇内的样本相似度较高&#xff0c;而不同簇之间的样本相似度较低。在数据分析中&#xff0c;聚类算法可以帮助我们发现数据的内在结构和规律&#xff0c;从而为进一步的数据分析…

工业以太网交换机引领现代工厂自动化新潮流

随着科技的飞速发展&#xff0c;现代工厂正迎来一场前所未有的自动化变革&#xff0c;而工业以太网交换机的崭新角色正是这场变革的关键组成部分。本文将深入探讨工业以太网交换机与现代工厂自动化的紧密集成&#xff0c;探讨这一集成如何推动工业生产的智能化、效率提升以及未…

【自定义序列化器】⭐️通过继承JsonSerializer和实现WebMvcConfigurer类完成自定义序列化

目录 前言 解决方案 具体实现 一、自定义序列化器 二、两种方式指定作用域 1、注解 JsonSerialize() 2、实现自定义全局配置 WebMvcConfigurer 三、拓展 WebMvcConfigurer接口 章末 前言 小伙伴们大家好&#xff0c;上次做了自定义对象属性拷贝&#x…

【LangChain-04】利用权重和偏差跟踪和检查LangChain代理的提示

利用权重和偏差跟踪和检查LangChain代理的提示 一、说明 考虑到&#xff08;生成&#xff09;人工智能空间&#xff0c;&#xff08;自主&#xff09;代理现在无处不在&#xff01;除了更强大且幸运的是开放的大型语言模型&#xff08;LLM&#xff09;之外&#xff0c;LangCh…