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

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

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

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


一.现象描述

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

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

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

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

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

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


二.源码分析

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 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

你可能感兴趣的:(android7,android11,内部存储,便携式存储,SD,formatFileSize,roundStorageSiz)