Android10-系统存储空间显示源码分析

简介: 笔记

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

2.StorageVolumeProvider


源码位置frameworks/base/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java

它是一个接口,实现类为frameworks/base/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java

/**
 * StorageVolumeProvider provides access to the storage volumes on a device for free space
 * calculations.
StorageVolumeProvider 提供设备上各个分卷可用空间的计算方法
 */
public interface StorageVolumeProvider {
    /** 
     * Returns the number of bytes of total storage on the primary storage.
        计算主分区的总大小 
     */
    long getPrimaryStorageSize();
    /** 
     * Returns a list of VolumeInfos for the device.
        返回分卷列表
     */
    List<VolumeInfo> getVolumes();
    /** 
     * Returns the emulated volume for a given private volume.
      返回指定私有卷对应的模拟卷
     */
    VolumeInfo findEmulatedForPrivate(VolumeInfo privateVolume);
    /** 
     * Returns the total bytes for a given storage volume.
     *返回指定分卷的大小
     * @pre The volume is a private volume and is readable.
     */
    long getTotalBytes(StorageStatsManager stats, VolumeInfo volume) throws IOException;
    /**
     * Returns the free bytes for a given storage volume.
     *返回指定分卷的可用空间大小
     * @pre The volume is a private volume and is readable.
     */
    long getFreeBytes(StorageStatsManager stats, VolumeInfo volume) throws IOException;
}

3.StorageManagerVolumeProvider


源码位置frameworks/base/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java

public class StorageManagerVolumeProvider implements StorageVolumeProvider {
    private StorageManager mStorageManager;
    //传入StorageManager作为参数,它是重点
    public StorageManagerVolumeProvider(StorageManager sm) {
        mStorageManager = sm; 
    }   
    @Override
    public long getPrimaryStorageSize() {
        return mStorageManager.getPrimaryStorageSize();
    }   
    @Override
    public List<VolumeInfo> getVolumes() {
        return mStorageManager.getVolumes();
    }   
    @Override
    public VolumeInfo findEmulatedForPrivate(VolumeInfo privateVolume) {
        return mStorageManager.findEmulatedForPrivate(privateVolume);
    }   
    @Override
    public long getTotalBytes(StorageStatsManager stats, VolumeInfo volume) throws IOException {
        return stats.getTotalBytes(volume.getFsUuid());
    }
    @Override
    public long getFreeBytes(StorageStatsManager stats, VolumeInfo volume) throws IOException {
        return stats.getFreeBytes(volume.getFsUuid());
    }
}

4.重点来了StorageManager


源码位置frameworks/base/core/java/android/os/storage/StorageManager.java

先分析下面三个方法,另外两个后面分析

private final IStorageManager mStorageManager;
   //获取总大小 
    public long getPrimaryStorageSize() {
      //总大小是用户空间+系统空间,并进行二次方化
        return FileUtils.roundStorageSize(Environment.getDataDirectory().getTotalSpace()
                + Environment.getRootDirectory().getTotalSpace());
    }
//获取所有卷标
   public @NonNull List<VolumeInfo> getVolumes() {
       try {
           return Arrays.asList(mStorageManager.getVolumes(0));
       } catch (RemoteException e) {
           throw e.rethrowFromSystemServer();
       }
   }
//查找私有卷的模拟卷
  public @Nullable VolumeInfo findEmulatedForPrivate(VolumeInfo privateVol) {
      if (privateVol != null) {
          return findVolumeById(privateVol.getId().replace("private", "emulated"));
      } else {
          return null;
      }
  }
//根据卷ID查找卷
 public @Nullable VolumeInfo findVolumeById(String id) {
     Preconditions.checkNotNull(id);
     // TODO; go directly to service to make this faster
     for (VolumeInfo vol : getVolumes()) {
         if (Objects.equals(vol.id, id)) {
             return vol;
         }
     }
     return null;
 }

5.getPrimaryStorageSize方法


源码位置:frameworks/base/core/java/android/os/FileUtils.java

/**
   * 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.
将存储设备的给定大小四舍五入为漂亮的 2 次方
    值,例如 256MB 或 32GB。这样可以避免显示奇怪的值,例如
    用户界面中的“29.5GB”。
   *
   * @hide
   */
  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;
  }

frameworks/base/core/java/android/os/Environment.java

private static final String ENV_ANDROID_ROOT = "ANDROID_ROOT";
 private static final String ENV_ANDROID_DATA = "ANDROID_DATA";
 public static final String DIR_ANDROID = "Android";
 private static final String DIR_DATA = "data";
//系统分区对应根目录/system
 private static final File DIR_ANDROID_ROOT = getDirectory(ENV_ANDROID_ROOT, "/system");
//用户分区对应根目录/data
 private static final File DIR_ANDROID_DATA = getDirectory(ENV_ANDROID_DATA, "/data");
  static File getDirectory(String variableName, String defaultPath) {
      String path = System.getenv(variableName); //获取是否有定义属性,见下面定义,一般是没有的
      return path == null ? new File(defaultPath) : new File(path);
  }
  /**
   * Return the user data directory.
返回用户空间
   */
  public static File getDataDirectory() {
      return DIR_ANDROID_DATA;
  }
 /**
  * Return root of the "system" partition holding the core Android OS.
  * Always present and mounted read-only.
  返回支行Android OS的system分区,该分区始终以只读的方式存在和挂载
  */
 public static @NonNull File getRootDirectory() {
     return DIR_ANDROID_ROOT;
 }

java.lang.System

/**
     * Returns the value of the environment variable with the given name, or null if no such
     * variable exists.
     */
    public static String getenv(String name) {
        if (name == null) {
            throw new NullPointerException("name == null");
        }
        return Libcore.os.getenv(name);
    }

/data分区和/system分区配置

源码位置:device/sprd/sharkle/sl8541e_1h10_32b/sl8541e_1h10_32b.xml

<Partitions>
       <!-- size unit is MBytes -->
       <Partition id="prodnv" size="10"/>
       <Partition id="miscdata" size="1"/>
       <Partition id="recovery" size="35"/>
       <Partition id="misc" size="1"/>
       <Partition id="trustos" size="6"/>
       <Partition id="trustos_bak" size="6"/>
       <Partition id="sml" size="1"/>
       <Partition id="sml_bak" size="1"/>
       <Partition id="uboot" size="1"/>
       <Partition id="uboot_bak" size="1"/>
       <Partition id="uboot_log" size="4"/>
       <Partition id="logo" size="4"/>
       <Partition id="fbootlogo" size="4"/>
       <Partition id="l_fixnv1" size="1"/>
       <Partition id="l_fixnv2" size="1"/>
       <Partition id="l_runtimenv1" size="1"/>
       <Partition id="l_runtimenv2" size="1"/>
       <Partition id="gpsgl" size="1"/>
       <Partition id="gpsbd" size="1"/>
       <Partition id="wcnmodem" size="10"/>
     <Partition id="persist"   size="2"/>
     <Partition id="l_modem" size="25"/>
     <Partition id="l_deltanv" size="1"/>
     <Partition id="l_gdsp" size="10"/>
     <Partition id="l_ldsp" size="20"/>
     <Partition id="pm_sys" size="1"/>
     <Partition id="boot" size="35"/>
     <Partition id="dtbo" size="8"/>
     <Partition id="super" size="3100"/> <!--system product vendor三个镜像+预留空间-->
     <Partition id="cache" size="150"/>
     <Partition id="socko" size="75"/>
     <Partition id="odmko" size="25"/>
     <Partition id="vbmeta" size="1"/>
     <Partition id="vbmeta_bak" size="1"/>
     <Partition id="sysdumpdb" size="10"/>
     <Partition id="metadata" size="16"/>
     <Partition id="vbmeta_system" size="1"/>
     <Partition id="vbmeta_vendor" size="1"/>
     <Partition id="userdata" size="0xFFFFFFFF"/>

device/sprd/sharkle/sl8541e_1h10_32b/BoardConfig.mk

sprdiskexist := $(shell if [ -f $(TOPDIR)sprdisk/Makefile -a "$(TARGET_BUILD_VARIANT)" = "userdebug" ]; then echo "exist"; else echo "notexist"; fi;)
ifneq ($(sprdiskexist), exist)
TARGET_NO_SPRDISK := true
else
TARGET_NO_SPRDISK := false
endif
SPRDISK_BUILD_PATH := sprdisk/
BOARD_SUPER_PARTITION_SIZE := 3250585600 #system product vendor三个镜像+预留空间
BOARD_GROUP_UNISOC_SIZE := 3250585600
# ext4 partition layout
#BOARD_VENDORIMAGE_PARTITION_SIZE := 314572800
BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE := ext4
TARGET_COPY_OUT_VENDOR=vendor
TARGET_USERIMAGES_USE_EXT4 := true
BOARD_BOOTIMAGE_PARTITION_SIZE := 36700160
BOARD_RECOVERYIMAGE_PARTITION_SIZE := 36700160
#BOARD_SYSTEMIMAGE_PARTITION_SIZE := 1625292800
BOARD_CACHEIMAGE_PARTITION_SIZE := 150000000
BOARD_PRODNVIMAGE_PARTITION_SIZE := 10485760
BOARD_USERDATAIMAGE_PARTITION_SIZE := 134217728
BOARD_DTBIMG_PARTITION_SIZE := 8388608
BOARD_DTBOIMG_PARTITION_SIZE := 8388608
BOARD_FLASH_BLOCK_SIZE := 4096
BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE := ext4
BOARD_PRODNVIMAGE_FILE_SYSTEM_TYPE := ext4
TARGET_SYSTEMIMAGES_SPARSE_EXT_DISABLED := true
TARGET_USERIMAGES_SPARSE_EXT_DISABLED := false
BOARD_PERSISTIMAGE_PARTITION_SIZE := 2097152
TARGET_PRODNVIMAGES_SPARSE_EXT_DISABLED := true
TARGET_CACHEIMAGES_SPARSE_EXT_DISABLED := false
USE_SPRD_SENSOR_HUB := false
#BOARD_PRODUCTIMAGE_PARTITION_SIZE :=419430400
BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE := ext4
TARGET_COPY_OUT_PRODUCT=product
BOARD_SOCKOIMAGE_FILE_SYSTEM_TYPE := ext4
BOARD_ODMKOIMAGE_FILE_SYSTEM_TYPE := ext4
BOARD_SOCKOIMAGE_PARTITION_SIZE := 78643200 # 75M
BOARD_ODMKOIMAGE_PARTITION_SIZE := 26214400 # 25M
#creates the metadata directory
BOARD_USES_METADATA_PARTITION := true

总结:修改除 system、cache、prodnv、data 之外的分区只需要修改工具中 project.xml 文件即可,修

改好 xml 文件之后请重新制作 pac 包,以确保修改成功


6.getVolumes方法


从5中看到,调用了AIDL接口,源码位置frameworks/base/services/core/java/com/android/server/StorageManagerService.java

/** Map from volume ID to disk */
//该结构体保存卷信息<VolumeInfo.id,VolumeInfo>
 private final ArrayMap<String, VolumeInfo> mVolumes = new ArrayMap<>();
//返回所有卷标
 @Override
 public VolumeInfo[] getVolumes(int flags) {
     synchronized (mLock) {
         final VolumeInfo[] res = new VolumeInfo[mVolumes.size()];
         for (int i = 0; i < mVolumes.size(); i++) {
             res[i] = mVolumes.valueAt(i);
         }
         return res;
     }
 }

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

/** Stub volume representing internal private storage */
 public static final String ID_PRIVATE_INTERNAL = "private";
 /** Real volume representing internal emulated storage */
 public static final String ID_EMULATED_INTERNAL = "emulated";
//类型,PUBLIC,PRIVATE ASEC,OBB,STUB
public static final int TYPE_PUBLIC = IVold.VOLUME_TYPE_PUBLIC;
public static final int TYPE_PRIVATE = IVold.VOLUME_TYPE_PRIVATE;
public static final int TYPE_EMULATED = IVold.VOLUME_TYPE_EMULATED;
public static final int TYPE_ASEC = IVold.VOLUME_TYPE_ASEC;
public static final int TYPE_OBB = IVold.VOLUME_TYPE_OBB;
public static final int TYPE_STUB = IVold.VOLUME_TYPE_STUB;
//主要有以下成员变量
  public final String id; //id
  public final int type; //权限类型
  public final DiskInfo disk; //磁盘信息
  public final String partGuid; //分区组ID
  public int mountFlags = 0; //挂载标志位
  public int mountUserId = UserHandle.USER_NULL; //用户
  public int state = STATE_UNMOUNTED; //挂载状态
  public String fsType; //文件系统类型
  public String fsUuid; //文件系统id
  public String fsLabel;//文件系统标签
  public String path; //路径
  public String internalPath;//内部内径
  /* SPRD: add for storage manage */
  public String linkName;
  /* @SPRD: add for UMS */
  public int stateBeforeUMS = STATE_UNMOUNTED;

7.接第3节,另外两个没有分析的方法


源码位置:frameworks/base/core/java/android/app/usage/StorageStatsManager.java

private final IStorageStatsManager mService; 
//获取指定文件系统的大小 stats.getTotalBytes(volume.getFsUuid());
 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();
     }
 }
//获取指定文件系统的可用空间大小 stats.getFreeBytes(volume.getFsUuid());
 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/services/usage/java/com/android/server/usage/StorageStatsService.java

private final StorageManager mStorage;
  @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) {
      // NOTE: No permissions required
      final long token = Binder.clearCallingIdentity();
      try {
          final File path;
          try {
              path = mStorage.findPathForUuid(volumeUuid);
          } catch (FileNotFoundException e) {
              throw new ParcelableException(e);
          }
          // Free space is usable bytes plus any cached data that we're
          // willing to automatically clear. To avoid user confusion, this
          // logic should be kept in sync with getAllocatableBytes().
          if (isQuotaSupported(volumeUuid, PLATFORM_PACKAGE_NAME)) {
              final long cacheTotal = getCacheBytes(volumeUuid, PLATFORM_PACKAGE_NAME);
              final long cacheReserved = mStorage.getStorageCacheBytes(path, 0);
              final long cacheClearable = Math.max(0, cacheTotal - cacheReserved);
              return path.getUsableSpace() + cacheClearable;
          } else {
              return path.getUsableSpace();
          }
      } finally {
          Binder.restoreCallingIdentity(token);
      }
  }

实际上还是调用的StorageManager进行操作,StorageManager又调用到了StorageManagerService,与前面分析流程基本一致。

目录
相关文章
|
2月前
|
Android开发
基于android-11.0.0_r39,系统应用的手动签名方法和过程
本文介绍了基于Android 11.0.0_r39版本进行系统应用手动签名的方法和解决签名过程中遇到的错误,包括处理`no conscrypt_openjdk_jni-linux-x86_64`和`RegisterNatives failed`的问题。
88 2
|
2月前
|
JavaScript 前端开发 Java
[Android][Framework]系统jar包,sdk的制作及引用
[Android][Framework]系统jar包,sdk的制作及引用
43 0
|
8天前
|
监控 Android开发 iOS开发
深入探索安卓与iOS的系统架构差异:理解两大移动平台的技术根基在移动技术日新月异的今天,安卓和iOS作为市场上最为流行的两个操作系统,各自拥有独特的技术特性和庞大的用户基础。本文将深入探讨这两个平台的系统架构差异,揭示它们如何支撑起各自的生态系统,并影响着全球数亿用户的使用体验。
本文通过对比分析安卓和iOS的系统架构,揭示了这两个平台在设计理念、安全性、用户体验和技术生态上的根本区别。不同于常规的技术综述,本文以深入浅出的方式,带领读者理解这些差异是如何影响应用开发、用户选择和市场趋势的。通过梳理历史脉络和未来展望,本文旨在为开发者、用户以及行业分析师提供有价值的见解,帮助大家更好地把握移动技术发展的脉络。
|
5天前
|
Dart 开发工具 Android开发
在 Android 系统上搭建 Flutter 环境的具体步骤是什么?
在 Android 系统上搭建 Flutter 环境的具体步骤是什么?
|
28天前
|
Android开发 UED 开发者
Android经典实战之WindowManager和创建系统悬浮窗
本文详细介绍了Android系统服务`WindowManager`,包括其主要功能和工作原理,并提供了创建系统悬浮窗的完整步骤。通过示例代码,展示了如何添加权限、请求权限、实现悬浮窗口及最佳实践,帮助开发者轻松掌握悬浮窗开发技巧。
55 1
|
2月前
|
存储 安全 API
Android经典实战之存储方案对比:SharedPreferences vs MMKV vs DataStore
本文介绍了 Android 开发中常用的键值对存储方案,包括 SharedPreferences、MMKV 和 DataStore,并对比了它们在性能、并发处理、易用性和稳定性上的特点。通过实际代码示例,帮助开发者根据项目需求选择最适合的存储方案,提升应用性能和用户体验。
47 1
|
2月前
|
Java 物联网 Android开发
移动应用与系统:技术演进与未来展望探索安卓应用开发:从新手到专家的旅程
【8月更文挑战第28天】本文将深入探讨移动应用开发的技术演进、移动操作系统的发展历程以及未来的发展趋势。我们将通过实例和代码示例,展示如何利用最新的技术和工具来开发高效、可靠的移动应用。无论你是初学者还是经验丰富的开发者,这篇文章都将为你提供有价值的信息和见解。 【8月更文挑战第28天】在这个数字时代,掌握安卓应用的开发技能不仅是技术人员的追求,也成为了许多人实现创意和梦想的途径。本文将通过深入浅出的方式,带领读者从零基础开始,一步步走进安卓开发的奇妙世界。我们将探讨如何配置开发环境,理解安卓应用的核心组件,以及如何通过实际编码来构建一个功能完整的应用。无论你是编程新手还是希望提升自己的开发者
|
2月前
|
安全 Android开发 iOS开发
安卓与iOS的终极对决:哪个系统更适合你?
在智能手机的世界里,安卓和iOS两大操作系统如同两座巍峨的山峰,各自拥有庞大的用户群体。本文将深入浅出地探讨这两个系统的优缺点,并帮助你找到最适合自己的那一款。让我们一起揭开这场技术盛宴的序幕吧!
|
XML 前端开发 Java
android 自定义控件,有无命名空间两种方法
引用:http://wujiandong.iteye.com/blog/1184921 属性(Attribute)资源:属于整个Android应用资源的一部分.其实就是网上一堆介绍怎么给自定义View添加自己的属性文章里的attrs文件,此文件位于../res/values/目录下 当别人通过XML文件配置的方式来创建你开发的自定义组件,并且还能动态设置你自定义组件的属性时,这时候你就需要给你自己自定义的组件配上一个XML属性资源文件来完成这项工作了. 其实也可以不配上一个XML属性资源文件,也能完成如上的功能,这样你的自定义组件显得更干脆一点,一个自定义组件就是一个类文件,不拖泥带水的。
894 0
|
4天前
|
IDE Android开发 iOS开发
探索Android与iOS开发的差异:平台选择对项目成功的影响
【9月更文挑战第27天】在移动应用开发的世界中,Android和iOS是两个主要的操作系统平台。每个系统都有其独特的开发环境、工具和用户群体。本文将深入探讨这两个平台的关键差异点,并分析这些差异如何影响应用的性能、用户体验和最终的市场表现。通过对比分析,我们将揭示选择正确的开发平台对于确保项目成功的重要作用。