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,与前面分析流程基本一致。

目录
相关文章
|
1月前
|
人工智能 搜索推荐 物联网
Android系统版本演进与未来展望####
本文深入探讨了Android操作系统从诞生至今的发展历程,详细阐述了其关键版本迭代带来的创新特性、用户体验提升及对全球移动生态系统的影响。通过对Android历史版本的回顾与分析,本文旨在揭示其成功背后的驱动力,并展望未来Android可能的发展趋势与面临的挑战,为读者呈现一个既全面又具深度的技术视角。 ####
|
29天前
|
IDE Java 开发工具
移动应用与系统:探索Android开发之旅
在这篇文章中,我们将深入探讨Android开发的各个方面,从基础知识到高级技术。我们将通过代码示例和案例分析,帮助读者更好地理解和掌握Android开发。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和技巧。让我们一起开启Android开发的旅程吧!
|
16天前
|
监控 Java Android开发
深入探索Android系统的内存管理机制
本文旨在全面解析Android系统的内存管理机制,包括其工作原理、常见问题及其解决方案。通过对Android内存模型的深入分析,本文将帮助开发者更好地理解内存分配、回收以及优化策略,从而提高应用性能和用户体验。
|
18天前
|
存储 安全 Android开发
探索Android系统的最新安全特性
在数字时代,智能手机已成为我们生活中不可或缺的一部分。随着技术的不断进步,手机操作系统的安全性也越来越受到重视。本文将深入探讨Android系统最新的安全特性,包括其设计理念、实施方式以及对用户的影响。通过分析这些安全措施如何保护用户免受恶意软件和网络攻击的威胁,我们希望为读者提供对Android安全性的全面了解。
|
1月前
|
监控 Java Android开发
深入探讨Android系统的内存管理机制
本文将深入分析Android系统的内存管理机制,包括其内存分配、回收策略以及常见的内存泄漏问题。通过对这些方面的详细讨论,读者可以更好地理解Android系统如何高效地管理内存资源,从而提高应用程序的性能和稳定性。
66 16
|
23天前
|
安全 Android开发 iOS开发
深入探讨Android与iOS系统的差异及未来发展趋势
本文旨在深入分析Android和iOS两大移动操作系统的核心技术差异、用户体验以及各自的市场表现,进一步探讨它们在未来技术革新中可能的发展方向。通过对比两者的开放性、安全性、生态系统等方面,本文揭示了两大系统在移动设备市场中的竞争态势和潜在变革。
|
1月前
|
算法 JavaScript Android开发
|
存储 Java Android开发
Android:StatFs类 获取系统/sdcard存储空间信息
在存储文件时,为了保证有充足的存储空间大小,通常需要知道系统内部或者sdcard的剩余存储空间大小,这里就需要用到StatFs类。 1. 判断 SDCard 是否存在,并且是否具有可读写权限  [java] view plaincopyprint? /** * 外部存储是...
1328 0
|
16天前
|
搜索推荐 前端开发 API
探索安卓开发中的自定义视图:打造个性化用户界面
在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
40 19
|
16天前
|
JSON Java API
探索安卓开发:打造你的首个天气应用
在这篇技术指南中,我们将一起潜入安卓开发的海洋,学习如何从零开始构建一个简单的天气应用。通过这个实践项目,你将掌握安卓开发的核心概念、界面设计、网络编程以及数据解析等技能。无论你是初学者还是有一定基础的开发者,这篇文章都将为你提供一个清晰的路线图和实用的代码示例,帮助你在安卓开发的道路上迈出坚实的一步。让我们一起开始这段旅程,打造属于你自己的第一个安卓应用吧!
41 14