美文网首页
Android10-系统存储空间显示源码分析

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

作者: DD_Dog | 来源:发表于2022-10-09 11:56 被阅读0次

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

    相关文章

      网友评论

          本文标题:Android10-系统存储空间显示源码分析

          本文链接:https://www.haomeiwen.com/subject/zlsgartx.html