Android 7.1 设置-存储信息显示不正确

简介: Android 7.1 设置-存储信息显示不正确

平台


RK3288 + Android 7.1


问题描述


设置中的存储信息显示与实际EMMC的大小相差太大. 如下图所示(16GB):

image.png

实际的显示效果应该是:

image.png


分析


在RK3368 Android7.1上, 显示的正是预期效果, 开始比对设置部分代码:

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


private void refresh() {
        final Context context = getPrefContext();
        getPreferenceScreen().removeAll();
        mInternalCategory.removeAll();
        mExternalCategory.removeAll();
        mInternalCategory.addPreference(mInternalSummary);
        int privateCount = 0;
        long privateUsedBytes = 0;
        long privateTotalBytes = 0;
        final List<VolumeInfo> volumes = mStorageManager.getVolumes();
        Collections.sort(volumes, VolumeInfo.getDescriptionComparator());
  //重点解读下面的代码, 结果发现, 代码3368与3288看不出区别.
        for (VolumeInfo vol : volumes) {//遍历所有的存储
            android.util.Log.d("StorageSettings", "ALog vol(" + vol.getPath() + ")");
            if (vol.getType() == VolumeInfo.TYPE_PRIVATE) {
                final long volumeTotalBytes = PrivateStorageInfo.getTotalSize(vol,
                        sTotalInternalStorage);
                final int color = COLOR_PRIVATE[privateCount++ % COLOR_PRIVATE.length];
                mInternalCategory.addPreference(
                        new StorageVolumePreference(context, vol, color, volumeTotalBytes));
                if (vol.isMountedReadable()) {
                    final File path = vol.getPath();
                    privateUsedBytes += (volumeTotalBytes - path.getFreeSpace());
                    privateTotalBytes += volumeTotalBytes;
                    //统计大小并显示.
                }
            } else if (vol.getType() == VolumeInfo.TYPE_PUBLIC) {
                mExternalCategory.addPreference(
                        new StorageVolumePreference(context, vol, COLOR_PUBLIC, 0));
            }
        }
        // Show missing private volumes
        final List<VolumeRecord> recs = mStorageManager.getVolumeRecords();
        for (VolumeRecord rec : recs) {
            if (rec.getType() == VolumeInfo.TYPE_PRIVATE
                    && mStorageManager.findVolumeByUuid(rec.getFsUuid()) == null) {
                // TODO: add actual storage type to record
                final Drawable icon = context.getDrawable(R.drawable.ic_sim_sd);
                icon.mutate();
                icon.setTint(COLOR_PUBLIC);
                final Preference pref = new Preference(context);
                pref.setKey(rec.getFsUuid());
                pref.setTitle(rec.getNickname());
                pref.setSummary(com.android.internal.R.string.ext_media_status_missing);
                pref.setIcon(icon);
                mInternalCategory.addPreference(pref);
            }
        }
        // Show unsupported disks to give a chance to init
        final List<DiskInfo> disks = mStorageManager.getDisks();
        for (DiskInfo disk : disks) {
            if (disk.volumeCount == 0 && disk.size > 0) {
                final Preference pref = new Preference(context);
                pref.setKey(disk.getId());
                pref.setTitle(disk.getDescription());
                pref.setSummary(com.android.internal.R.string.ext_media_status_unsupported);
                pref.setIcon(R.drawable.ic_sim_sd);
                mExternalCategory.addPreference(pref);
            }
        }
        final BytesResult result = Formatter.formatBytes(getResources(), privateUsedBytes, 0);
        mInternalSummary.setTitle(TextUtils.expandTemplate(getText(R.string.storage_size_large),
                result.value, result.units));
        mInternalSummary.setSummary(getString(R.string.storage_volume_used_total,
                Formatter.formatFileSize(context, privateTotalBytes)));
        if (mInternalCategory.getPreferenceCount() > 0) {
            getPreferenceScreen().addPreference(mInternalCategory);
        }
        if (mExternalCategory.getPreferenceCount() > 0) {
            getPreferenceScreen().addPreference(mExternalCategory);
        }
        if (mInternalCategory.getPreferenceCount() == 2
                && mExternalCategory.getPreferenceCount() == 0) {
            // Only showing primary internal storage, so just shortcut
            final Bundle args = new Bundle();
            args.putString(VolumeInfo.EXTRA_VOLUME_ID, VolumeInfo.ID_PRIVATE_INTERNAL);
            PrivateVolumeSettings.setVolumeSize(args, sTotalInternalStorage);
            Intent intent = Utils.onBuildStartFragmentIntent(getActivity(),
                    PrivateVolumeSettings.class.getName(), args, null, R.string.apps_storage, null,
                    false);
            intent.putExtra(SettingsDrawerActivity.EXTRA_SHOW_MENU, true);
            getActivity().startActivity(intent);
            finish();
        }
    }


获取系统存储信息:


final List<VolumeInfo> volumes = mStorageManager.getVolumes();


代码没有任何问题, 在LOG中输出privateTotalBytes的最终结果上看正是UI中显示的大小信息.


大小计算:


final long volumeTotalBytes = PrivateStorageInfo.getTotalSize(vol,
                        sTotalInternalStorage);
//frameworks/base/packages/SettingsLib/src/com/android/settingslib/deviceinfo/PrivateStorageInfo.java
    /**
     * Returns the total size in bytes for a given volume info.
     * @param info Info of the volume to check.
     * @param totalInternalStorage Total number of bytes in the internal storage to use if the
     *                             volume is the internal disk.
     */
    public static long getTotalSize(VolumeInfo info, long totalInternalStorage) {
        // Device could have more than one primary storage, which could be located in the
        // internal flash (UUID_PRIVATE_INTERNAL) or in an external disk.
        // If it's internal, try to get its total size from StorageManager first
        // (totalInternalStorage), because that size is more precise because it accounts for
        // the system partition.
        if (info.getType() == VolumeInfo.TYPE_PRIVATE
                && Objects.equals(info.getFsUuid(), StorageManager.UUID_PRIVATE_INTERNAL)
                && totalInternalStorage > 0) {
            return totalInternalStorage;
        } else {
            final File path = info.getPath();
            if (path == null) {
                // Should not happen, caller should have checked.
                Log.e(TAG, "info's path is null on getTotalSize(): " + info);
                return 0;
            }
            return path.getTotalSpace();
        }
    }
}


由于 File.getTotalSpace() 所得出的大小始终不对, 于是, 从上面的函数入口, 着手查privateTotalBytes的值

当然, 从上面的代码和注释已可基本推断: privateTotalBytes 应该是要找的正确的值.



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


frameworks/base/core/java/android/os/storage/StorageManager.java


// TODO: the location of the primary storage block varies from device to device, so we need to
    // try the most likely candidates - a long-term solution would be a device-specific vold
    // function that returns the calculated size.
    private static final String[] INTERNAL_STORAGE_SIZE_PATHS = {
            //"/sys/block/mmcblk0/size",
            //"/sys/block/sda/size"
    };
    private static final String[] INTERNAL_STORAGE_SIZE_PATHS_ALTERNATIVE = {
            "/sys/block/mmcblk1/size",
            "/sys/block/sda/size",
     "/sys/block/rknand_userdata/size"
    };
  //从这里我感觉, 3288被鄙视了
    private static boolean alternative_path=platform.equals("rk3368")||platform.equals("rk3399");
    /** {@hide} */
    public long getPrimaryStorageSize() {
        if(alternative_path){//RK3368 和 RK3399
            for (String path : INTERNAL_STORAGE_SIZE_PATHS_ALTERNATIVE) {
                final long numberBlocks = readLong(path);
                if (numberBlocks > 0) {
                    return numberBlocks * INTERNAL_STORAGE_SECTOR_SIZE;
                }
            }
        }
        else{//RK3288
          //INTERNAL_STORAGE_SIZE_PATHS 这个数组是空的, 元素被注释掉.
            for (String path : INTERNAL_STORAGE_SIZE_PATHS) {
                final long numberBlocks = readLong(path);
                if (numberBlocks > 0) {
                    return numberBlocks * INTERNAL_STORAGE_SECTOR_SIZE;
                }
            }
        }
        return 0;
    }
    private long readLong(String path) {
        try (final FileInputStream fis = new FileInputStream(path);
                final BufferedReader reader = new BufferedReader(new InputStreamReader(fis));) {
            return Long.parseLong(reader.readLine());
        } catch (Exception e) {
            Slog.w(TAG, "Could not read " + path, e);
            return 0;
        }
    }


getPrimaryStorageSize 在RK3368返回正确的存储大小, RK3288返回0


再来确认下, 对应的size文件在3288上是否存在或有效:


rk3288:/ # ll /sys/block/
total 0
lrwxrwxrwx 1 root root 0 2017-01-01 21:55 loop0 -> ../devices/virtual/block/loop0
lrwxrwxrwx 1 root root 0 2017-01-01 21:55 loop1 -> ../devices/virtual/block/loop1
lrwxrwxrwx 1 root root 0 2017-01-01 21:55 loop2 -> ../devices/virtual/block/loop2
lrwxrwxrwx 1 root root 0 2017-01-01 21:55 loop3 -> ../devices/virtual/block/loop3
lrwxrwxrwx 1 root root 0 2017-01-01 21:55 loop4 -> ../devices/virtual/block/loop4
lrwxrwxrwx 1 root root 0 2017-01-01 21:55 loop5 -> ../devices/virtual/block/loop5
lrwxrwxrwx 1 root root 0 2017-01-01 21:55 loop6 -> ../devices/virtual/block/loop6
lrwxrwxrwx 1 root root 0 2017-01-01 21:55 loop7 -> ../devices/virtual/block/loop7
lrwxrwxrwx 1 root root 0 2017-01-01 21:55 mmcblk2 -> ../devices/platform/ff0f0000.dwmmc/mmc_host/mmc2/mmc2:0001/block/mmcblk2
lrwxrwxrwx 1 root root 0 2017-01-01 21:55 mmcblk2boot0 -> ../devices/platform/ff0f0000.dwmmc/mmc_host/mmc2/mmc2:0001/block/mmcblk2/mmcblk2boot0
lrwxrwxrwx 1 root root 0 2017-01-01 21:55 mmcblk2boot1 -> ../devices/platform/ff0f0000.dwmmc/mmc_host/mmc2/mmc2:0001/block/mmcblk2/mmcblk2boot1
lrwxrwxrwx 1 root root 0 2017-01-01 21:55 mmcblk2rpmb -> ../devices/platform/ff0f0000.dwmmc/mmc_host/mmc2/mmc2:0001/block/mmcblk2/mmcblk2rpmb
lrwxrwxrwx 1 root root 0 2017-01-01 21:55 ram0 -> ../devices/virtual/block/ram0
lrwxrwxrwx 1 root root 0 2017-01-01 21:55 ram1 -> ../devices/virtual/block/ram1
lrwxrwxrwx 1 root root 0 2017-01-01 21:55 ram10 -> ../devices/virtual/block/ram10
lrwxrwxrwx 1 root root 0 2017-01-01 21:55 ram11 -> ../devices/virtual/block/ram11
lrwxrwxrwx 1 root root 0 2017-01-01 21:55 ram12 -> ../devices/virtual/block/ram12
lrwxrwxrwx 1 root root 0 2017-01-01 21:55 ram13 -> ../devices/virtual/block/ram13
lrwxrwxrwx 1 root root 0 2017-01-01 21:55 ram14 -> ../devices/virtual/block/ram14
lrwxrwxrwx 1 root root 0 2017-01-01 21:55 ram15 -> ../devices/virtual/block/ram15
lrwxrwxrwx 1 root root 0 2017-01-01 21:55 ram2 -> ../devices/virtual/block/ram2
lrwxrwxrwx 1 root root 0 2017-01-01 21:55 ram3 -> ../devices/virtual/block/ram3
lrwxrwxrwx 1 root root 0 2017-01-01 21:55 ram4 -> ../devices/virtual/block/ram4
lrwxrwxrwx 1 root root 0 2017-01-01 21:55 ram5 -> ../devices/virtual/block/ram5
lrwxrwxrwx 1 root root 0 2017-01-01 21:55 ram6 -> ../devices/virtual/block/ram6
lrwxrwxrwx 1 root root 0 2017-01-01 21:55 ram7 -> ../devices/virtual/block/ram7
lrwxrwxrwx 1 root root 0 2017-01-01 21:55 ram8 -> ../devices/virtual/block/ram8
lrwxrwxrwx 1 root root 0 2017-01-01 21:55 ram9 -> ../devices/virtual/block/ram9
lrwxrwxrwx 1 root root 0 2017-01-01 21:55 zram0 -> ../devices/virtual/block/zram0
rk3288:/ # 
/ff0f0000.dwmmc/mmc_host/mmc2/mmc2:0001/block/mmcblk2/size                    <
30535680


30535680 * 512 = 15634268160 = 14.56GB


于是, 改下代码:


private static final String[] INTERNAL_STORAGE_SIZE_PATHS = {
            "/sys/block/mmcblk1/size",//8GB
            "/sys/block/mmcblk2/size",//16GB
            //"/sys/block/sda/size"
    };


相关文章
|
6月前
|
Android开发 开发者
Android设置View是否可用
在Android开发中,有时需要将布局设置为不可点击状态(失去焦点)。常见的解决方法是使用`setOnClickListener(null)`,但本文介绍一种更通用的方式:通过封装`setViewEnabled`方法实现。该方法可递归设置View及其子View的启用状态,支持传入目标View和布尔值(`true`为可用,`false`为禁用)。例如,调用`setViewEnabled(edittext, false)`即可禁用EditText。文章附有源码及示例动图,帮助开发者快速理解与应用。
133 1
|
6月前
|
Android开发 开发者
Android中Dialog位置+样式的设置
本文介绍了在Android开发中如何设置Dialog的位置和样式。通过自定义`MyDialog`类,可以灵活调整Dialog的显示位置,例如将其固定在屏幕底部,并设置宽度匹配父布局。同时,文章还展示了如何模仿Android原生Dialog样式,通过定义`MyDialogStyle`去除标题栏、设置背景透明度、添加阴影效果以及配置点击外部关闭等功能,从而实现更加美观和符合需求的Dialog效果。代码示例详细,便于开发者快速上手实现。
383 2
|
XML API Android开发
码农之重学安卓:利用androidx.preference 快速创建一、二级设置菜单(demo)
本文介绍了如何使用androidx.preference库快速创建具有一级和二级菜单的Android设置界面的步骤和示例代码。
451 1
码农之重学安卓:利用androidx.preference 快速创建一、二级设置菜单(demo)
|
存储 API 文件存储
47. 【Android教程】SharedPreferences 存储
47. 【Android教程】SharedPreferences 存储
203 2
|
Java Unix Linux
Android Studio中Terminal运行./gradlew clean build提示错误信息
遇到 `./gradlew clean build`命令执行出错时,首先应检查错误信息的具体内容,这通常会指向问题的根源。从权限、环境配置、依赖下载、版本兼容性到项目配置本身,逐一排查并应用相应的解决措施。记住,保持耐心,逐步解决问题,往往复杂问题都是由简单原因引起的。
1105 2
|
Android开发
Android经典实战之Textview文字设置不同颜色、下划线、加粗、超链接等效果
本文介绍了 `SpannableString` 在 Android 开发中的强大功能,包括如何在单个字符串中应用多种样式,如颜色、字体大小、风格等,并提供了详细代码示例,展示如何设置文本颜色、添加点击事件等,助你实现丰富文本效果。
996 4
|
存储 安全 API
Android经典实战之存储方案对比:SharedPreferences vs MMKV vs DataStore
本文介绍了 Android 开发中常用的键值对存储方案,包括 SharedPreferences、MMKV 和 DataStore,并对比了它们在性能、并发处理、易用性和稳定性上的特点。通过实际代码示例,帮助开发者根据项目需求选择最适合的存储方案,提升应用性能和用户体验。
849 1
|
XML Android开发 数据格式
Android 中如何设置activity的启动动画,让它像dialog一样从底部往上出来
在 Android 中实现 Activity 的对话框式过渡动画:从底部滑入与从顶部滑出。需定义两个 XML 动画文件 `activity_slide_in.xml` 和 `activity_slide_out.xml`,分别控制 Activity 的进入与退出动画。使用 `overridePendingTransition` 方法在启动 (`startActivity`) 或结束 (`finish`) Activity 时应用这些动画。为了使前 Activity 保持静止,可定义 `no_animation.xml` 并在启动新 Activity 时仅设置新 Activity 的进入动画。
596 12
|
Java 网络安全 开发工具
UNITY与安卓⭐一、Android Studio初始设置
UNITY与安卓⭐一、Android Studio初始设置
|
Java Android开发
android 设置系统时间的流程
android 设置系统时间的方法
953 2

热门文章

最新文章