1. Android 8.1 1.1. 涉及的文件
1.2. 相关代码 计算方式:
totalSize = stats.(codeSize + externalCodeSize + dataSize + externalDataSize + cacheSize +externalCacheSize );
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 public void updateUi (Context context) { if (mLastResult == null ) { ... } else { long codeSize = mLastResult.getCodeBytes(); long dataSize = mDataCleared ? 0 : mLastResult.getDataBytes() - mLastResult.getCacheBytes(); if (mLastCodeSize != codeSize) { mLastCodeSize = codeSize; mAppSize.setSummary(getSizeStr(context, codeSize)); } if (mLastDataSize != dataSize) { mLastDataSize = dataSize; mDataSize.setSummary(getSizeStr(context, dataSize)); } long cacheSize = (mDataCleared || mCachedCleared) ? 0 : mLastResult.getCacheBytes(); if (mLastCacheSize != cacheSize) { mLastCacheSize = cacheSize; mCacheSize.setSummary(getSizeStr(context, cacheSize)); } long totalSize = codeSize + dataSize + cacheSize; if (mLastTotalSize != totalSize) { mLastTotalSize = totalSize; mTotalSize.setSummary(getSizeStr(context, totalSize)); } } } updateUiWithSize(mSizeController.getLastResult()); @Override public void onLoadFinished (Loader<AppStorageStats> loader, AppStorageStats result) { mSizeController.setResult(result); updateUiWithSize(result); } @Override public AppStorageStats loadInBackground () { AppStorageStats result = null ; try { result = mSource.getStatsForPackage(mInfo.volumeUuid, mInfo.packageName, mUser); } catch (NameNotFoundException | IOException e) { Log.w(TAG, "Package may have been removed during query, failing gracefully" , e); } return result; } mInfo = packageManager.getApplicationInfo(mPackageName, 0 ); public StorageStats queryStatsForPackage (String volumeUuid, String packageName, int userId, String callingPackage) { ... final ApplicationInfo appInfo; try { appInfo = mPackage.getApplicationInfoAsUser(packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId); } if (defeatNullable(mPackage.getPackagesForUid(appInfo.uid)).length == 1 ) { return queryStatsForUid(volumeUuid, appInfo.uid, callingPackage); } else { final int appId = UserHandle.getUserId(appInfo.uid); final String[] packageNames = new String [] { packageName }; final long [] ceDataInodes = new long [1 ]; String[] codePaths = new String [0 ]; if (appInfo.isSystemApp() && !appInfo.isUpdatedSystemApp()) { } else { codePaths = ArrayUtils.appendElement(String.class, codePaths, appInfo.getCodePath()); } final PackageStats stats = new PackageStats (TAG); try { mInstaller.getAppSize(volumeUuid, packageNames, userId, 0 , appId, ceDataInodes, codePaths, stats); } return translate(stats); } } private static StorageStats translate (PackageStats stats) { final StorageStats res = new StorageStats (); res.codeBytes = stats.codeSize + stats.externalCodeSize; res.dataBytes = stats.dataSize + stats.externalDataSize; res.cacheBytes = stats.cacheSize + stats.externalCacheSize; return res; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 @Override public StorageStats queryStatsForUid (String volumeUuid, int uid, String callingPackage) { final int userId = UserHandle.getUserId(uid); final int appId = UserHandle.getAppId(uid); if (userId != UserHandle.getCallingUserId()) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.INTERACT_ACROSS_USERS, TAG); } if (Binder.getCallingUid() == uid) { } else { enforcePermission(Binder.getCallingUid(), callingPackage); } final String[] packageNames = defeatNullable(mPackage.getPackagesForUid(uid)); final long [] ceDataInodes = new long [packageNames.length]; String[] codePaths = new String [0 ]; for (int i = 0 ; i < packageNames.length; i++) { try { final ApplicationInfo appInfo = mPackage.getApplicationInfoAsUser(packageNames[i], PackageManager.MATCH_UNINSTALLED_PACKAGES, userId); if (appInfo.isSystemApp() && !appInfo.isUpdatedSystemApp()) { } else { codePaths = ArrayUtils.appendElement(String.class, codePaths, appInfo.getCodePath()); } } catch (NameNotFoundException e) { throw new ParcelableException (e); } } final PackageStats stats = new PackageStats (TAG); try { mInstaller.getAppSize(volumeUuid, packageNames, userId, getDefaultFlags(), appId, ceDataInodes, codePaths, stats); } catch (InstallerException e) { throw new ParcelableException (new IOException (e.getMessage())); } return translate(stats); }
最终执行的代码为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 codePaths = appInfo.getCodePath()); mInstaller.getAppSize(volumeUuid, packageNames, userId, getDefaultFlags(), appId, ceDataInodes, codePaths, stats); public void getAppSize (String uuid, String[] packageNames, int userId, int flags, int appId, long [] ceDataInodes, String[] codePaths, PackageStats stats) throws InstallerException { if (!checkBeforeRemote()) return ; try { final long [] res = mInstalld.getAppSize(uuid, packageNames, userId, flags, appId, ceDataInodes, codePaths); stats.codeSize += res[0 ]; stats.dataSize += res[1 ]; stats.cacheSize += res[2 ]; ... stats.externalCodeSize += res[3 ]; stats.externalDataSize += res[4 ]; stats.externalCacheSize += res[5 ]; } catch (Exception e) { throw InstallerException.from(e); } }
执行进入installd中, 关注 getAppSize的实现.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 struct stats stats;auto device = findQuotaDeviceForUuid (uuid);if (device.empty ()) { flags &= ~FLAG_USE_QUOTA; } ATRACE_BEGIN ("obb" );for (auto packageName : packageNames) { auto obbCodePath = create_data_media_obb_path (uuid_, packageName.c_str ()); calculate_tree_size (obbCodePath, &extStats.codeSize); } ATRACE_END ();if (flags & FLAG_USE_QUOTA && appId >= AID_APP_START) { ATRACE_BEGIN ("code" ); for (auto codePath : codePaths) { calculate_tree_size (codePath, &stats.codeSize, -1 , multiuser_get_shared_gid (0 , appId)); } ATRACE_END (); ATRACE_BEGIN ("quota" ); collectQuotaStats (device, userId, appId, &stats, &extStats); ATRACE_END (); } else { ATRACE_BEGIN ("code" ); for (auto codePath : codePaths) { calculate_tree_size (codePath, &stats.codeSize); } ATRACE_END (); for (size_t i = 0 ; i < packageNames.size (); i++) { const char * pkgname = packageNames[i].c_str (); ATRACE_BEGIN ("data" ); auto cePath = create_data_user_ce_package_path (uuid_, userId, pkgname, ceDataInodes[i]); collectManualStats (cePath, &stats); auto dePath = create_data_user_de_package_path (uuid_, userId, pkgname); collectManualStats (dePath, &stats); ATRACE_END (); if (!uuid) { ATRACE_BEGIN ("profiles" ); calculate_tree_size ( create_primary_current_profile_package_dir_path (userId, pkgname), &stats.dataSize); calculate_tree_size ( create_primary_reference_profile_package_dir_path (pkgname), &stats.codeSize); ATRACE_END (); } ATRACE_BEGIN ("external" ); auto extPath = create_data_media_package_path (uuid_, userId, "data" , pkgname); collectManualStats (extPath, &extStats); auto mediaPath = create_data_media_package_path (uuid_, userId, "media" , pkgname); calculate_tree_size (mediaPath, &extStats.dataSize); ATRACE_END (); } if (!uuid) { ATRACE_BEGIN ("dalvik" ); int32_t sharedGid = multiuser_get_shared_gid (0 , appId); if (sharedGid != -1 ) { calculate_tree_size (create_data_dalvik_cache_path (), &stats.codeSize, sharedGid, -1 ); } ATRACE_END (); } } std::vector<int64_t > ret; ret.push_back (stats.codeSize); ret.push_back (stats.dataSize); ret.push_back (stats.cacheSize); ret.push_back (extStats.codeSize); ret.push_back (extStats.dataSize); ret.push_back (extStats.cacheSize);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 static void collectQuotaStats (const std::string& device, int32_t userId, int32_t appId, struct stats* stats, struct stats* extStats) { if (device.empty ()) return ; struct dqblk dq; if (stats != nullptr ) { uid_t uid = multiuser_get_uid (userId, appId); if (quotactl (QCMD (Q_GETQUOTA, USRQUOTA), device.c_str (), uid, reinterpret_cast <char *>(&dq)) != 0 ) { } else { stats->dataSize += dq.dqb_curspace; } int cacheGid = multiuser_get_cache_gid (userId, appId); if (cacheGid != -1 ) { if (quotactl (QCMD (Q_GETQUOTA, GRPQUOTA), device.c_str (), cacheGid, reinterpret_cast <char *>(&dq)) != 0 ) { } else { stats->cacheSize += dq.dqb_curspace; } } int sharedGid = multiuser_get_shared_gid (0 , appId); if (sharedGid != -1 ) { if (quotactl (QCMD (Q_GETQUOTA, GRPQUOTA), device.c_str (), sharedGid, reinterpret_cast <char *>(&dq)) != 0 ) { } else { stats->codeSize += dq.dqb_curspace; } } } if (extStats != nullptr ) { int extGid = multiuser_get_ext_gid (userId, appId); if (extGid != -1 ) { if (quotactl (QCMD (Q_GETQUOTA, GRPQUOTA), device.c_str (), extGid, reinterpret_cast <char *>(&dq)) != 0 ) { if (errno != ESRCH) { PLOG (ERROR) << "Failed to quotactl " << device << " for GID " << extGid; } } else { #if MEASURE_DEBUG LOG (DEBUG) << "quotactl() for GID " << extGid << " " << dq.dqb_curspace; #endif extStats->dataSize += dq.dqb_curspace; } } int extCacheGid = multiuser_get_ext_cache_gid (userId, appId); if (extCacheGid != -1 ) { if (quotactl (QCMD (Q_GETQUOTA, GRPQUOTA), device.c_str (), extCacheGid, reinterpret_cast <char *>(&dq)) != 0 ) { if (errno != ESRCH) { PLOG (ERROR) << "Failed to quotactl " << device << " for GID " << extCacheGid; } } else { #if MEASURE_DEBUG LOG (DEBUG) << "quotactl() for GID " << extCacheGid << " " << dq.dqb_curspace; #endif extStats->dataSize += dq.dqb_curspace; extStats->cacheSize += dq.dqb_curspace; } } } }
1.2.1. 没有开启quota的情况下
统计codePaths的大小, 放入codeSize
中. 预制在system分区的应用codeSize不统计大小
统计ce区 De区的应用的所占空间大小, 目录为data/user_ce|user_de/user_id/packageName,
cache目录和code_cache放到 cacheSize
中, 其他的放到dataSize
中.如果lib 是链接, 忽略, 不是链接, 计入dataSize
中
/data/misc/profiles/cur/user_id/packageName计入dataSize
中, /data/misc/profiles/ref/packageName计入codeSize
中.
/data/dalvik-cache/下且文件所属用户组 为 appId - 10000 + 50000的, 计入codeSize
中. appid>=0且appid<10000的, 统计所属用户组为appid为文件.
/data/media/obb/packageName/ 下的文件大小,计入 extStats.codeSize 中
/data/media/user_id/Android/data/PackageName 下的大小, 计入extStats.dataSize中, cache|code_cache下放到extStats.cacheSize
/data/media/user_id/Android/media/PackageName 下的 计入extStats.dataSize中.
1.2.2. 开启quota的情况
data/media/obb/packageName/ 下的文件大小,计入 extStats.codeSize 中
统计codePath 的大小, 计入codeSize, 排除gid为 appid - 10000 + 50000的share gid的文件统计,appid>=0且appid<10000的, 统计所属用户组为appid为文件.
查询quota中记录的 uid/cachegid/sharedgid 的使用量.
2. Android7.0 2.1. 涉及的文件
ApplicationState.java
AppStorageSettings.java
PackageManagerService.java
2.2. 相关代码 AppStorageSettings activity界面负责应用存储信息的展示.
onResume时会对该app的各大小进行统计
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 public void onResume () { super.onResume (); mState.requestSize (mPackageName, mUserId); } public void requestSize (String packageName, int userId) { synchronized (mEntriesMap) { AppEntry entry = mEntriesMap.get (userId).get (packageName); if (entry != null) { mPm.getPackageSizeInfoAsUser (packageName, userId, mBackgroundHandler.mStatsObserver); } } } final IPackageStatsObserver.Stub mStatsObserver = new IPackageStatsObserver.Stub () { public void onGetStatsCompleted (PackageStats stats, boolean succeeded) { boolean sizeChanged = false ; synchronized (mEntriesMap) { if (DEBUG_LOCKING) Log.v (TAG, "onGetStatsCompleted acquired lock" ); HashMap<String, AppEntry> userMap = mEntriesMap.get (stats.userHandle); AppEntry entry = userMap.get (stats.packageName); if (entry != null) { synchronized (entry) { entry.sizeStale = false ; entry.sizeLoadStart = 0 ; long externalCodeSize = stats.externalCodeSize + stats.externalObbSize; long externalDataSize = stats.externalDataSize + stats.externalMediaSize; long newSize = externalCodeSize + externalDataSize + getTotalInternalSize (stats); if (entry.size != newSize || entry.cacheSize != stats.cacheSize || entry.codeSize != stats.codeSize || entry.dataSize != stats.dataSize || entry.externalCodeSize != externalCodeSize || entry.externalDataSize != externalDataSize || entry.externalCacheSize != stats.externalCacheSize) { entry.size = newSize; entry.cacheSize = stats.cacheSize; entry.codeSize = stats.codeSize; entry.dataSize = stats.dataSize; entry.externalCodeSize = externalCodeSize; entry.externalDataSize = externalDataSize; entry.externalCacheSize = stats.externalCacheSize; entry.sizeStr = getSizeStr (entry.size); entry.internalSize = getTotalInternalSize (stats); entry.internalSizeStr = getSizeStr (entry.internalSize); entry.externalSize = getTotalExternalSize (stats); entry.externalSizeStr = getSizeStr (entry.externalSize); if (DEBUG) Log.i (TAG, "Set size of " + entry.label + " " + entry + ": " + entry.sizeStr); sizeChanged = true ; } } ... } } }; private void refreshSizeInfo () { if (mAppEntry.size == ApplicationsState.SIZE_INVALID || mAppEntry.size == ApplicationsState.SIZE_UNKNOWN) { ... } else { mHaveSizes = true ; long codeSize = mAppEntry.codeSize; long dataSize = mAppEntry.dataSize; if (Environment.isExternalStorageEmulated ()) { codeSize += mAppEntry.externalCodeSize; dataSize += mAppEntry.externalDataSize; } else { if (mLastExternalCodeSize != mAppEntry.externalCodeSize) { mLastExternalCodeSize = mAppEntry.externalCodeSize; mExternalCodeSize.setSummary (getSizeStr (mAppEntry.externalCodeSize)); } if (mLastExternalDataSize != mAppEntry.externalDataSize) { mLastExternalDataSize = mAppEntry.externalDataSize; mExternalDataSize.setSummary (getSizeStr ( mAppEntry.externalDataSize)); } } if (mLastCodeSize != codeSize) { mLastCodeSize = codeSize; mAppSize.setSummary (getSizeStr (codeSize)); } if (mLastDataSize != dataSize) { mLastDataSize = dataSize; mDataSize.setSummary (getSizeStr (dataSize)); } long cacheSize = mAppEntry.cacheSize + mAppEntry.externalCacheSize; if (mLastCacheSize != cacheSize) { mLastCacheSize = cacheSize; mCacheSize.setSummary (getSizeStr (cacheSize)); } if (mLastTotalSize != mAppEntry.size) { mLastTotalSize = mAppEntry.size; mTotalSize.setSummary (getSizeStr (mAppEntry.size)); } if ((mAppEntry.dataSize+ mAppEntry.externalDataSize) <= 0 || !mCanClearData) { mClearDataButton.setEnabled (false ); } else { mClearDataButton.setEnabled (true ); mClearDataButton.setOnClickListener (this ); } if (cacheSize <= 0 ) { mClearCacheButton.setEnabled (false ); } else { mClearCacheButton.setEnabled (true ); mClearCacheButton.setOnClickListener (this ); } } if (mAppsControlDisallowedBySystem) { mClearCacheButton.setEnabled (false ); mClearDataButton.setEnabled (false ); } }
7.0 上的 totalSize的计算方式:
stats.(externalCodeSize + externalObbSize + externalDataSize + externalMediaSize + codeSize + dataSize)
回到之前的核心方法:
getPackageSizeInfoAsUser(packageName, userId, mBackgroundHandler.mStatsObserver)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 @Override public void getPackageSizeInfo (final String packageName, int userHandle, final IPackageStatsObserver observer) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.GET_PACKAGE_SIZE, null ); PackageStats stats = new PackageStats (packageName, userHandle); Message msg = mHandler.obtainMessage(INIT_COPY); msg.obj = new MeasureParams (stats, observer); mHandler.sendMessage(msg); } case INIT_COPY: mPendingInstalls.add(idx, params); void handleStartCopy () throws RemoteException { synchronized (mInstallLock) { mSuccess = getPackageSizeInfoLI(mStats.packageName, mStats.userHandle, mStats); } if (mSuccess) { boolean mounted = false ; try { final String status = EnvironmentEx.getExternalStoragePathState(); mounted = (Environment.MEDIA_MOUNTED.equals(status) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(status)); } if (mounted) { final UserEnvironment userEnv = new UserEnvironment (mStats.userHandle); mStats.externalCacheSize = calculateDirectorySize(mContainerService, userEnv.buildExternalStorageAppCacheDirs(mStats.packageName)); mStats.externalDataSize = calculateDirectorySize(mContainerService, userEnv.buildExternalStorageAppDataDirs(mStats.packageName)); mStats.externalDataSize -= mStats.externalCacheSize; mStats.externalMediaSize = calculateDirectorySize(mContainerService, userEnv.buildExternalStorageAppMediaDirs(mStats.packageName)); mStats.externalObbSize = calculateDirectorySize(mContainerService, userEnv.buildExternalStorageAppObbDirs(mStats.packageName)); } } } private boolean getPackageSizeInfoLI (String packageName, int userId, PackageStats stats) { final PackageSetting ps; synchronized (mPackages) { ps = mSettings.mPackages.get(packageName); if (ps == null ) { Slog.w(TAG, "Failed to find settings for " + packageName); return false ; } } try { mInstaller.getAppSize(ps.volumeUuid, packageName, userId, StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE, ps.getCeDataInode(userId), ps.codePathString, stats); } if (isSystemApp(ps) && !isUpdatedSystemApp(ps)) { stats.codeSize = 0 ; } return true ; }
最终落到Installd统计app的internal 的 codeSize和 dataSize
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 int get_app_size (const char *uuid, const char *pkgname, int userid, int flags, ino_t ce_data_inode, const char *code_path, int64_t *codesize, int64_t *datasize, int64_t *cachesize, int64_t * asecsize) { DIR *d; int dfd; d = opendir (code_path); if (d != nullptr ) { dfd = dirfd (d); *codesize += calculate_dir_size (dfd); closedir (d); } if (flags & FLAG_STORAGE_CE) { auto path = create_data_user_ce_package_path (uuid, userid, pkgname, ce_data_inode); add_app_data_size (path, codesize, datasize, cachesize); } if (flags & FLAG_STORAGE_DE) { auto path = create_data_user_de_package_path (uuid, userid, pkgname); add_app_data_size (path, codesize, datasize, cachesize); } *asecsize = 0 ; return 0 ; } static void add_app_data_size (std::string& path, int64_t *codesize, int64_t *datasize, int64_t *cachesize) { DIR *d; int dfd; struct dirent *de; struct stat s; d = opendir (path.c_str ()); if (d == nullptr ) { PLOG (WARNING) << "Failed to open " << path; return ; } dfd = dirfd (d); while ((de = readdir (d))) { const char *name = de->d_name; int64_t statsize = 0 ; if (fstatat (dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0 ) { statsize = stat_size (&s); } if (de->d_type == DT_DIR) { int subfd; int64_t dirsize = 0 ; if (name[0 ] == '.' ) { if (name[1 ] == 0 ) continue ; if ((name[1 ] == '.' ) && (name[2 ] == 0 )) continue ; } subfd = openat (dfd, name, O_RDONLY | O_DIRECTORY); if (subfd >= 0 ) { dirsize = calculate_dir_size (subfd); close (subfd); } if (!strcmp (name, "cache" ) || !strcmp (name, "code_cache" )) { *datasize += statsize; *cachesize += dirsize; } else { *datasize += dirsize + statsize; } } else if (de->d_type == DT_LNK && !strcmp (name, "lib" )) { *codesize += statsize; } else { *datasize += statsize; } } closedir (d); }
综上, Android7.0的统计app总使用量的大小为:
stats.(externalCodeSize + externalObbSize + externalDataSize + externalMediaSize + codeSize + dataSize)
计算codeSize 即package的 codePaths 目录大小
统计CE区的 /data/user_ce/user_id/packageName/下的, 除cache/code_cache目录外的大小. 计入dataSize (codeSize可以忽略)
统计DE区的 /data/user_de/user_id/packageName/下的, 除cache/code_cache目录外的大小. 计入dataSize
统计所有可写存储的 Android/data/packageName/大小, 除cache子目录外, 计入 externalDataSize
统计所有可写存储的 Android/media/packageName/,计入externalMediaSize
统计所有可写存储的 Android/obb/packageName/,计入externalObbSize
externalCodeSize没看到有计算的地方
3. Android6.0 3.1. 涉及的文件
AppStorageSettings.java
ApplicationState.java
PackageManagerService.java
3.2. 相关代码 计算方式和7.0相同:
stats.(externalCodeSize + externalObbSize + externalDataSize + externalMediaSize + codeSize + dataSize)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 public void requestSize (String packageName, int userId) { if (DEBUG_LOCKING) Log.v(TAG, "requestSize about to acquire lock..." ); synchronized (mEntriesMap) { AppEntry entry = mEntriesMap.get(userId).get(packageName); if (entry != null ) { mPm.getPackageSizeInfo(packageName, userId, mBackgroundHandler.mStatsObserver); } if (DEBUG_LOCKING) Log.v(TAG, "...requestSize releasing lock" ); } } final IPackageStatsObserver.Stub mStatsObserver = new IPackageStatsObserver .Stub() { ... long newSize = externalCodeSize + externalDataSize + getTotalInternalSize(stats); } @Override public void getPackageSizeInfo (final String packageName, int userHandle, final IPackageStatsObserver observer) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.GET_PACKAGE_SIZE, null ); PackageStats stats = new PackageStats (packageName, userHandle); Message msg = mHandler.obtainMessage(INIT_COPY); msg.obj = new MeasureParams (stats, observer); mHandler.sendMessage(msg); } final String status = Environment.getExternalStoragePathState();mounted = (Environment.MEDIA_MOUNTED.equals(status) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(status)); } if (mounted) { final UserEnvironment userEnv = new UserEnvironment (mStats.userHandle); mStats.externalCacheSize = calculateDirectorySize(mContainerService, userEnv.buildExternalStorageAppCacheDirs(mStats.packageName)); mStats.externalDataSize = calculateDirectorySize(mContainerService, userEnv.buildExternalStorageAppDataDirs(mStats.packageName)); mStats.externalDataSize -= mStats.externalCacheSize; mStats.externalMediaSize = calculateDirectorySize(mContainerService, userEnv.buildExternalStorageAppMediaDirs(mStats.packageName)); mStats.externalObbSize = calculateDirectorySize(mContainerService, userEnv.buildExternalStorageAppObbDirs(mStats.packageName)); } private boolean getPackageSizeInfoLI (String packageName, int userHandle, PackageStats pStats) { if (packageName == null ) { Slog.w(TAG, "Attempt to get size of null packageName." ); return false ; } PackageParser.Package p; boolean dataOnly = false ; String libDirRoot = null ; String asecPath = null ; PackageSetting ps = null ; synchronized (mPackages) { p = mPackages.get(packageName); ps = mSettings.mPackages.get(packageName); if (p == null ) { dataOnly = true ; p = ps.pkg; } if (ps != null ) { libDirRoot = ps.legacyNativeLibraryPathString; } if (p != null && (isExternal(p) || p.isForwardLocked() || isInternalSd(p))) { final long token = Binder.clearCallingIdentity(); try { String secureContainerId = cidFromCodePath(p.applicationInfo.getBaseCodePath()); if (secureContainerId != null ) { asecPath = PackageHelper.getSdFilesystem(secureContainerId); } } } } String publicSrcDir = null ; if (!dataOnly) { final ApplicationInfo applicationInfo = p.applicationInfo; if (p.isForwardLocked()) { publicSrcDir = applicationInfo.getBaseResourcePath(); } } String[] dexCodeInstructionSets = getDexCodeInstructionSets(getAppDexInstructionSets(ps)); int res = mInstaller.getSizeInfo(p.volumeUuid, packageName, userHandle, p.baseCodePath, libDirRoot, publicSrcDir, asecPath, dexCodeInstructionSets, pStats); if (res < 0 ) { return false ; } if (!isExternal(p) && !isInternalSd(p)) { pStats.codeSize += pStats.externalCodeSize; pStats.externalCodeSize = 0L ; } return true ; } try { pStats.codeSize = Long.parseLong(res[1 ]); pStats.dataSize = Long.parseLong(res[2 ]); pStats.cacheSize = Long.parseLong(res[3 ]); pStats.externalCodeSize = Long.parseLong(res[4 ]); return Integer.parseInt(res[0 ]); }
native层的代码, 在installd中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 int get_size (const char *uuid, const char *pkgname, int userid, const char *apkpath, const char *libdirpath, const char *fwdlock_apkpath, const char *asecpath, const char *instruction_set, int64_t *_codesize, int64_t *_datasize, int64_t *_cachesize, int64_t * _asecsize) { DIR *d; int dfd; struct dirent *de; struct stat s; char path[PKG_PATH_MAX]; int64_t codesize = 0 ; int64_t datasize = 0 ; int64_t cachesize = 0 ; int64_t asecsize = 0 ; if (validate_system_app_path (apkpath) && strncmp (apkpath, android_asec_dir.path, android_asec_dir.len) != 0 ) { if (stat (apkpath, &s) == 0 ) { codesize += stat_size (&s); if (S_ISDIR (s.st_mode)) { d = opendir (apkpath); if (d != NULL ) { dfd = dirfd (d); codesize += calculate_dir_size (dfd); closedir (d); } } } } if (fwdlock_apkpath != NULL && fwdlock_apkpath[0 ] != '!' ) { if (stat (fwdlock_apkpath, &s) == 0 ) { codesize += stat_size (&s); } } if (!create_cache_path (path, apkpath, instruction_set)) { if (stat (path, &s) == 0 ) { codesize += stat_size (&s); } } if (libdirpath != NULL && libdirpath[0 ] != '!' ) { d = opendir (libdirpath); if (d != NULL ) { dfd = dirfd (d); codesize += calculate_dir_size (dfd); closedir (d); } } if (asecpath != NULL && asecpath[0 ] != '!' ) { if (stat (asecpath, &s) == 0 ) { asecsize += stat_size (&s); } } std::vector<userid_t > users; if (userid == -1 ) { users = get_known_users (uuid); } else { users.push_back (userid); } for (auto user : users) { std::string _pkgdir(create_data_user_package_path (uuid, user, pkgname)); const char * pkgdir = _pkgdir.c_str (); d = opendir (pkgdir); if (d == NULL ) { PLOG (WARNING) << "Failed to open " << pkgdir; continue ; } dfd = dirfd (d); while ((de = readdir (d))) { const char *name = de->d_name; if (de->d_type == DT_DIR) { int subfd; int64_t statsize = 0 ; int64_t dirsize = 0 ; if (name[0 ] == '.' ) { if (name[1 ] == 0 ) continue ; if ((name[1 ] == '.' ) && (name[2 ] == 0 )) continue ; } if (fstatat (dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0 ) { statsize = stat_size (&s); } subfd = openat (dfd, name, O_RDONLY | O_DIRECTORY); if (subfd >= 0 ) { dirsize = calculate_dir_size (subfd); } if (!strcmp (name,"lib" )) { codesize += dirsize + statsize; } else if (!strcmp (name,"cache" )) { cachesize += dirsize + statsize; } else { datasize += dirsize + statsize; } } else if (de->d_type == DT_LNK && !strcmp (name,"lib" )) { if (fstatat (dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0 ) { codesize += stat_size (&s); } } else { if (fstatat (dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0 ) { datasize += stat_size (&s); } } } closedir (d); } *_codesize = codesize; *_datasize = datasize; *_cachesize = cachesize; *_asecsize = asecsize; return 0 ; }
综上, Android6.0 上统计app totalsize方式:
stats.(externalCodeSize + externalObbSize + externalDataSize + externalMediaSize + codeSize + dataSize)
apk 没有在外部存储上, 统计codepath的 codesize
foward_lock时, codepath 和 respath分开,统计respath大小计入codesize
统计 /data/dalvik-cache//{/classes.dex}(“{}里的’/‘被转换为‘@’”), 计入codesize中.
如果apk安装在外部存储上, 统计asecsize, 最后计为 externalCodeSize
统计/data/user//下的文件大小, 计入codesize/cachesize/datasize
如为lib目录非链接,统计目录大小, 计入codesize
如为cache目录,统计目录大小, 计入cachesize
如为其他目录,统计目录大小, 计入datasize
lib目录且为链接, 这部分很小, 可以忽略不计, 但还是计入codesize
非目录, 普通文件, 计入datasize
统计所有可写存储的 Android/data/packageName/大小, 除cache子目录外, 计入 externalDataSize
统计所有可写存储的 Android/media/packageName/,计入externalMediaSize
统计所有可写存储的 Android/obb/packageName/,计入externalObbSize
4. Android5.1 4.1. 涉及的文件
InstalledAppDetails.java
PackageManagerService.java
4.2. 相关代码 计算方式:
stats.(externalCodeSize + externalObbSize + externalDataSize + externalMediaSize + codesize + datasize)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 private void refreshSizeInfo () { if (mAppEntry.size == ApplicationsState.SIZE_INVALID || mAppEntry.size == ApplicationsState.SIZE_UNKNOWN) { ... } else { mHaveSizes = true ; long codeSize = mAppEntry.codeSize; long dataSize = mAppEntry.dataSize; if (Environment.isExternalStorageEmulated()) { codeSize += mAppEntry.externalCodeSize; dataSize += mAppEntry.externalDataSize; } else { if (mLastExternalCodeSize != mAppEntry.externalCodeSize) { mLastExternalCodeSize = mAppEntry.externalCodeSize; mExternalCodeSize.setText(getSizeStr(mAppEntry.externalCodeSize)); } if (mLastExternalDataSize != mAppEntry.externalDataSize) { mLastExternalDataSize = mAppEntry.externalDataSize; mExternalDataSize.setText(getSizeStr( mAppEntry.externalDataSize)); } } if (mLastCodeSize != codeSize) { mLastCodeSize = codeSize; if (mAppSize != null ) { mAppSize.setText(getSizeStr(codeSize)); } } if (mLastDataSize != dataSize) { mLastDataSize = dataSize; mDataSize.setText(getSizeStr(dataSize)); } long cacheSize = mAppEntry.cacheSize + mAppEntry.externalCacheSize; if (cacheSize > 0 ) { cacheSize = cacheSize - 12 * 1024 ; } if (mLastCacheSize != cacheSize) { mLastCacheSize = cacheSize; mCacheSize.setText(getSizeStr(cacheSize)); } if (mLastTotalSize != mAppEntry.size) { mLastTotalSize = mAppEntry.size; mTotalSize.setText(getSizeStr(mAppEntry.size)); } } } mCurComputingSizePkg = entry.info.packageName; mPm.getPackageSizeInfo(mCurComputingSizePkg, mStatsObserver); void requestSize (String packageName) { if (DEBUG_LOCKING) Log.v(TAG, "requestSize about to acquire lock..." ); synchronized (mEntriesMap) { AppEntry entry = mEntriesMap.get(packageName); if (entry != null ) { mPm.getPackageSizeInfo(packageName, mBackgroundHandler.mStatsObserver); } if (DEBUG_LOCKING) Log.v(TAG, "...requestSize releasing lock" ); } } final IPackageStatsObserver.Stub mStatsObserver = new IPackageStatsObserver .Stub() { public void onGetStatsCompleted (PackageStats stats, boolean succeeded) { synchronized (mEntriesMap) { AppEntry entry = mEntriesMap.get(stats.packageName); if (entry != null ) { synchronized (entry) { entry.sizeStale = false ; entry.sizeLoadStart = 0 ; long externalCodeSize = stats.externalCodeSize + stats.externalObbSize; long externalDataSize = stats.externalDataSize + stats.externalMediaSize; long newSize = externalCodeSize + externalDataSize + getTotalInternalSize(stats); if (entry.size != newSize || entry.cacheSize != stats.cacheSize || entry.codeSize != stats.codeSize || entry.dataSize != stats.dataSize || entry.externalCodeSize != externalCodeSize || entry.externalDataSize != externalDataSize || entry.externalCacheSize != stats.externalCacheSize) { entry.size = newSize; entry.cacheSize = stats.cacheSize; entry.codeSize = stats.codeSize; entry.dataSize = stats.dataSize; entry.externalCodeSize = externalCodeSize; entry.externalDataSize = externalDataSize; entry.externalCacheSize = stats.externalCacheSize; entry.sizeStr = getSizeStr(entry.size); entry.internalSize = getTotalInternalSize(stats); entry.internalSizeStr = getSizeStr(entry.internalSize); entry.externalSize = getTotalExternalSize(stats); entry.externalSizeStr = getSizeStr(entry.externalSize); if (DEBUG) Log.i(TAG, "Set size of " + entry.label + " " + entry + ": " + entry.sizeStr); sizeChanged = true ; } }; void handleStartCopy () throws RemoteException { synchronized (mInstallLock) { mSuccess = getPackageSizeInfoLI(mStats.packageName, mStats.userHandle, mStats); } if (mSuccess) { final boolean mounted; if (Environment.isExternalStorageEmulated()) { mounted = true ; } else { final String status = Environment.getExternalStoragePathState(); mounted = (Environment.MEDIA_MOUNTED.equals(status) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(status)); } if (mounted) { final UserEnvironment userEnv = new UserEnvironment (mStats.userHandle); mStats.externalCacheSize = calculateDirectorySize(mContainerService, userEnv.buildExternalStorageAppCacheDirs(mStats.packageName)); mStats.externalDataSize = calculateDirectorySize(mContainerService, userEnv.buildExternalStorageAppDataDirs(mStats.packageName)); mStats.externalDataSize -= mStats.externalCacheSize; mStats.externalMediaSize = calculateDirectorySize(mContainerService, userEnv.buildExternalStorageAppMediaDirs(mStats.packageName)); mStats.externalObbSize = calculateDirectorySize(mContainerService, userEnv.buildExternalStorageAppObbDirs(mStats.packageName)); } } private boolean getPackageSizeInfoLI (String packageName, int userHandle, PackageStats pStats) { if (packageName == null ) { Slog.w(TAG, "Attempt to get size of null packageName." ); return false ; } PackageParser.Package p; boolean dataOnly = false ; String libDirRoot = null ; String asecPath = null ; PackageSetting ps = null ; synchronized (mPackages) { p = mPackages.get(packageName); ps = mSettings.mPackages.get(packageName); if (p == null ) { dataOnly = true ; if ((ps == null ) || (ps.pkg == null )) { Slog.w(TAG, "Package named '" + packageName +"' doesn't exist." ); return false ; } p = ps.pkg; } if (ps != null ) { libDirRoot = ps.legacyNativeLibraryPathString; } if (p != null && (isExternal(p) || isForwardLocked(p) || isInternalSd(p))) { String secureContainerId = cidFromCodePath(p.applicationInfo.getBaseCodePath()); if (secureContainerId != null ) { asecPath = PackageHelper.getSdFilesystem(secureContainerId); } } } String publicSrcDir = null ; if (!dataOnly) { final ApplicationInfo applicationInfo = p.applicationInfo; if (applicationInfo == null ) { Slog.w(TAG, "Package " + packageName + " has no applicationInfo." ); return false ; } if (isForwardLocked(p)) { publicSrcDir = applicationInfo.getBaseResourcePath(); } } String[] dexCodeInstructionSets = getDexCodeInstructionSets(getAppDexInstructionSets(ps)); int res = mInstaller.getSizeInfo(packageName, userHandle, p.baseCodePath, libDirRoot, publicSrcDir, asecPath, dexCodeInstructionSets, pStats); if (res < 0 ) { return false ; } if (!isExternal(p) && !isInternalSd(p)) { pStats.codeSize += pStats.externalCodeSize; pStats.externalCodeSize = 0L ; } return true ; }
综上, Android5.1 上 total_size 的计算方式和 Android6.0的完全相同. 此处不再赘述.
5. Android4.4 5.1. 涉及的文件
InstalledAppDetails.java
ApplicationState.java
5.2. 相关代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 mTotalSize.setText(getSizeStr(mAppEntry.size)); final IPackageStatsObserver.Stub mStatsObserver = new IPackageStatsObserver .Stub() { public void onGetStatsCompleted (PackageStats stats, boolean succeeded) { boolean sizeChanged = false ; synchronized (mEntriesMap) { AppEntry entry = mEntriesMap.get(stats.packageName); if (entry != null ) { synchronized (entry) { entry.sizeStale = false ; entry.sizeLoadStart = 0 ; long externalCodeSize = stats.externalCodeSize + stats.externalObbSize; long externalDataSize = stats.externalDataSize + stats.externalMediaSize; long newSize = externalCodeSize + externalDataSize + getTotalInternalSize(stats); if (entry.size != newSize || entry.cacheSize != stats.cacheSize || entry.codeSize != stats.codeSize || entry.dataSize != stats.dataSize || entry.externalCodeSize != externalCodeSize || entry.externalDataSize != externalDataSize || entry.externalCacheSize != stats.externalCacheSize) { entry.size = newSize; entry.cacheSize = stats.cacheSize; entry.codeSize = stats.codeSize; entry.dataSize = stats.dataSize; entry.externalCodeSize = externalCodeSize; entry.externalDataSize = externalDataSize; entry.externalCacheSize = stats.externalCacheSize; entry.sizeStr = getSizeStr(entry.size); entry.internalSize = getTotalInternalSize(stats); entry.internalSizeStr = getSizeStr(entry.internalSize); entry.externalSize = getTotalExternalSize(stats); entry.externalSizeStr = getSizeStr(entry.externalSize); } } } }; void handleStartCopy () throws RemoteException { synchronized (mInstallLock) { mSuccess = getPackageSizeInfoLI(mStats.packageName, mStats.userHandle, mStats); } final boolean mounted; if (Environment.isExternalStorageEmulated()) { mounted = true ; } else { final String status = Environment.getExternalStoragePathState(); mounted = (Environment.MEDIA_MOUNTED.equals(status) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(status)); } if (mounted) { final UserEnvironment userEnv = new UserEnvironment (mStats.userHandle); mStats.externalCacheSize = calculateDirectorySize(mContainerService, userEnv.buildExternalStorageAppCacheDirs(mStats.packageName)); mStats.externalDataSize = calculateDirectorySize(mContainerService, userEnv.buildExternalStorageAppDataDirs(mStats.packageName)); mStats.externalDataSize -= mStats.externalCacheSize; mStats.externalMediaSize = calculateDirectorySize(mContainerService, userEnv.buildExternalStorageAppMediaDirs(mStats.packageName)); mStats.externalObbSize = calculateDirectorySize(mContainerService, userEnv.buildExternalStorageAppObbDirs(mStats.packageName)); } } private boolean getPackageSizeInfoLI (String packageName, int userHandle, PackageStats pStats) { if (packageName == null ) { Slog.w(TAG, "Attempt to get size of null packageName." ); return false ; } PackageParser.Package p; boolean dataOnly = false ; String libDirPath = null ; String asecPath = null ; synchronized (mPackages) { p = mPackages.get(packageName); PackageSetting ps = mSettings.mPackages.get(packageName); if (p == null ) { dataOnly = true ; if ((ps == null ) || (ps.pkg == null )) { Slog.w(TAG, "Package named '" + packageName +"' doesn't exist." ); return false ; } p = ps.pkg; } if (ps != null ) { libDirPath = ps.nativeLibraryPathString; } if (p != null && (isExternal(p) || isForwardLocked(p) || isInternalSd(p))) { String secureContainerId = cidFromCodePath(p.applicationInfo.sourceDir); if (secureContainerId != null ) { asecPath = PackageHelper.getSdFilesystem(secureContainerId); } } } String publicSrcDir = null ; if (!dataOnly) { final ApplicationInfo applicationInfo = p.applicationInfo; if (applicationInfo == null ) { Slog.w(TAG, "Package " + packageName + " has no applicationInfo." ); return false ; } if (isForwardLocked(p)) { publicSrcDir = applicationInfo.publicSourceDir; } } int res = mInstaller.getSizeInfo(packageName, userHandle, p.mPath, libDirPath, publicSrcDir, asecPath, pStats); if (res < 0 ) { return false ; } if (!isExternal(p) && !isInternalSd(p)) { pStats.codeSize += pStats.externalCodeSize; pStats.externalCodeSize = 0L ; } return true ; }
综上, Android4.4 与 Android5.0 / Android6.0的统计方式是一致的, 不再赘述.