0%

统计app 总使用空间

1. Android 8.1

1.1. 涉及的文件

  • StorageStats.java

  • AppStorageSettings.java

  • InstalledAppDetails.java

  • StorageStatsManager.java

  • StorageStatsService.java

  • PackageManagerService.java

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));
}
// codeBytes + dataBytes + cacheBytes
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);

// 访问 StorageStatsService的 queryStatsForPackage
public StorageStats queryStatsForPackage(String volumeUuid, String packageName, int userId,
String callingPackage) {
...

final ApplicationInfo appInfo;
try {
appInfo = mPackage.getApplicationInfoAsUser(packageName,
PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
}

// 以SharedUserSetting 区分, 查找shareUser, 且ps.getInstalled(userId) = true的,
if (defeatNullable(mPackage.getPackagesForUid(appInfo.uid)).length == 1) {
// 只有一个时, 直接调用 queryStatsForUid函数
// Only one package inside UID means we can fast-path
return queryStatsForUid(volumeUuid, appInfo.uid, callingPackage);
} else {
// Multiple packages means we need to go manual
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()) {
// We don't count code baked into system image
// 不计算system下的app size
} 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) {
// No permissions required when asking about themselves
} else {
enforcePermission(Binder.getCallingUid(), callingPackage);
}

final String[] packageNames = defeatNullable(mPackage.getPackagesForUid(uid));
final long[] ceDataInodes = new long[packageNames.length];
String[] codePaths = new String[0];

//上面做过检验, defeatNullable(mPackage.getPackagesForUid(appInfo.uid)).length == 1), 即
// packageNames.length = 1;
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()) {
// We don't count code baked into system image
} 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;
// 是否开启quota
auto device = findQuotaDeviceForUuid(uuid);
if (device.empty()) {
flags &= ~FLAG_USE_QUOTA;
}

ATRACE_BEGIN("obb");
for (auto packageName : packageNames) {
// data/media/obb/packageName/ 下的文件大小,计入 extStats.codeSize 中
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) {
// 统计codePath 的大小, 计入codeSize排除gid为 appid - 10000 + 50000的share gid的文件统计
calculate_tree_size(codePath, &stats.codeSize, -1,
multiuser_get_shared_gid(0, appId));
}
ATRACE_END();

ATRACE_BEGIN("quota");
// current usage for user or group id
// 查询quota中记录的 user/cachegid/sharedgid 的使用量.
collectQuotaStats(device, userId, appId, &stats, &extStats);
ATRACE_END();
} else {
// 没有开启quota的情况:
ATRACE_BEGIN("code");
// 统计codePaths的大小
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");
// data/user_ce/user_id/packageName
auto cePath = create_data_user_ce_package_path(uuid_, userId, pkgname, ceDataInodes[i]);
collectManualStats(cePath, &stats);
// data/user_de/user_id/packageName
auto dePath = create_data_user_de_package_path(uuid_, userId, pkgname);
// 统计上面两个目录的大小, cache code_cache下的放到 cache_size中,其他的放到data_size中.
// 如果lib 是链接, 忽略, 不是链接, 计入 code_size中
collectManualStats(dePath, &stats);
ATRACE_END();

if (!uuid) {
ATRACE_BEGIN("profiles");
// /data/misc/profiles/cur/user_id/packageName
calculate_tree_size(
create_primary_current_profile_package_dir_path(userId, pkgname),
&stats.dataSize);
// /data/misc/profiles/ref/packageName
calculate_tree_size(
create_primary_reference_profile_package_dir_path(pkgname),
&stats.codeSize);
ATRACE_END();
}
ATRACE_BEGIN("external");
// /data/media/<user_id>/Android/data/PackageName 下的大小, 计入extStats.dataSize中, cache|code_cache下放到extStats.cacheSize
auto extPath = create_data_media_package_path(uuid_, userId, "data", pkgname);
collectManualStats(extPath, &extStats);
// /data/media/<user_id>/Android/media/PackageName 下的 计入extStats.dataSize中.
auto mediaPath = create_data_media_package_path(uuid_, userId, "media", pkgname);
calculate_tree_size(mediaPath, &extStats.dataSize);
ATRACE_END();
}

if (!uuid) {
ATRACE_BEGIN("dalvik");
// appId - 10000 + 50000
int32_t sharedGid = multiuser_get_shared_gid(0, appId);
if (sharedGid != -1) {
// /data/dalvik-cache/ 且文件所属用户组 为 appId - 10000 + 50000的,
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
// current usage for user or group id
// 查询quota中记录的 user/cachegid/sharedgid 的使用量. 放入 data_size cache_size data_size
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) {
// userid * 100000 + appid % 100000
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;
}
// userid * 100000 + appid - 10000 + 20000
// cacheGid 实际指向的目录 还是CE|DE 区的 cache code_cache目录
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;
}
}
// userid * 100000 + appid - 10000 + 50000
// sharegid指向 /data/misc/profiles/<packageName>
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) {
// 指向 /storage/emulated/<userid>/Android/obb|data|media/<packageName>, 计入 extStats->dataSize中, 除去cache目录
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;
}
}

// 指向 /storage/emulated/<userid>/Android/obb|data|media/<packageName>/cache目录, 计入 extStats->dataSize 和 cacheSize? 这个地方是不是重复了?
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) {
// 调用getPackageSizeInfoAsUser函数进行查询, 查询的结果放在mStatsObserver中进行处理
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;
// newSize的计算
// newSize = stats.(externalCodeSize + externalObbSize + externalDataSize
// + externalMediaSize + codeSize + dataSize)
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对应的 AppEntry
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;
// 主存储是否是Emulated的, 内卡主卡方案满足条件的
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;
// 将TotalSize 设为了 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);
//最终执行MeasureParams的 handleStartCopy方法:

void handleStartCopy() throws RemoteException {
synchronized (mInstallLock) {
mSuccess = getPackageSizeInfoLI(mStats.packageName, mStats.userHandle, mStats);
}

if (mSuccess) {
boolean mounted = false;
try {
/* SPRD: support double sdcard
* chanage interface, getExternalStorageState->getExternalStoragePathState
*/
final String status = EnvironmentEx.getExternalStoragePathState();
mounted = (Environment.MEDIA_MOUNTED.equals(status)
|| Environment.MEDIA_MOUNTED_READ_ONLY.equals(status));
}
// 如果外部存储是挂载状态, 还要接着计算 externalCacheSize externalDataSize externalMediaSize externalObbSize, 在计算方式中, 除externalCacheSize外都用到了
if (mounted) {
final UserEnvironment userEnv = new UserEnvironment(mStats.userHandle);
// 统计所有可写存储的 Android/data/packageName/cache
mStats.externalCacheSize = calculateDirectorySize(mContainerService,
userEnv.buildExternalStorageAppCacheDirs(mStats.packageName));
// 统计所有可写存储的 Android/data/packageName/
mStats.externalDataSize = calculateDirectorySize(mContainerService,
userEnv.buildExternalStorageAppDataDirs(mStats.packageName));

// Always subtract cache size, since it's a subdirectory
mStats.externalDataSize -= mStats.externalCacheSize;
// 统计所有可写存储的 Android/media/packageName/
mStats.externalMediaSize = calculateDirectorySize(mContainerService,
userEnv.buildExternalStorageAppMediaDirs(mStats.packageName));
// 统计所有可写存储的 Android/obb/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);
}
// For now, ignore code size of packages on system partition
// system下的app 忽略 codeSize
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 即package的 codePaths 目录大小
*codesize += calculate_dir_size(dfd);
closedir(d);
}

if (flags & FLAG_STORAGE_CE) {
// 统计CE区的 /data/user_ce/user_id/packageName/下的
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) {
// 统计CE区的 /data/user_ce/user_id/packageName/下的
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;
/* always skip "." and ".." */
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);
}
// TODO: check xattrs!
// dataSize中不包含 cache code_cache下 子文件的大小.
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")) {
// 如果是lib目录,且为链接的方式, 该目录的大小统计进了 codeSize中,但仅链接的话,statsize很小,可以忽略.
*codesize += statsize;
} else {
// 普通文件, 统计进dataSize中.
*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
// 前面的流程与7.0大致相同.   
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));

// Always subtract cache size, since it's a subdirectory
mStats.externalDataSize -= mStats.externalCacheSize;

mStats.externalMediaSize = calculateDirectorySize(mContainerService,
userEnv.buildExternalStorageAppMediaDirs(mStats.packageName));

mStats.externalObbSize = calculateDirectorySize(mContainerService,
userEnv.buildExternalStorageAppObbDirs(mStats.packageName));
}

// 上述流程都与7.0的一致, 但getPackageSizeInfoLI 函数和 7.0的不同

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) {
// native lib库的路径
libDirRoot = ps.legacyNativeLibraryPathString;
}
/* SPRD: support double sdcard
* Add support for install apk to internal sdcard @{
* @orig
* if (p != null && (isExternal(p) || p.isForwardLocked())) {
*/
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
// /mnt/secure/asec/<id.asec> 目录
asecPath = PackageHelper.getSdFilesystem(secureContainerId);
}
}
}
}
String publicSrcDir = null;
if(!dataOnly) {
final ApplicationInfo applicationInfo = p.applicationInfo;
// isForwardLocked为true时, code和resource分离, 需要拿到resource的路径
if (p.isForwardLocked()) {
publicSrcDir = applicationInfo.getBaseResourcePath();
}
}
// TODO: extend to measure size of split APKs
String[] dexCodeInstructionSets = getDexCodeInstructionSets(getAppDexInstructionSets(ps));
// 传入的路径 baseCodePath libDirRoot publicSrcDir asecPath dexCodeInstructionSets
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)) {
/* @} */
// 安装在内部存储上的应用, 将externalCodeSize 计入codeSize中.
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]);
// externalCodeSize 指的asecsize
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;

/* count the source apk as code -- but only if it's not
* on the /system partition and its not on the sdcard. */
if (validate_system_app_path(apkpath) &&
strncmp(apkpath, android_asec_dir.path, android_asec_dir.len) != 0) {
// apk 没有在外部存储上, 统计codepath的 codesize, 不统计system分区的.
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);
}
}
}
}

/* count the forward locked apk as code if it is given */
// foward_lock时, codepath 和 respath分开,统计respath大小计入codesize
if (fwdlock_apkpath != NULL && fwdlock_apkpath[0] != '!') {
if (stat(fwdlock_apkpath, &s) == 0) {
codesize += stat_size(&s);
}
}

/* count the cached dexfile as code */
// 统计 /data/dalvik-cache/<instruction_set>/[<codepath>/classes.dex]("[]里的'/'被转换为'@'")
// 计入codesize中.
if (!create_cache_path(path, apkpath, instruction_set)) {
if (stat(path, &s) == 0) {
codesize += stat_size(&s);
}
}

/* add in size of any libraries */
// 将package关联的native lib库的路径下大小计入codesize
if (libdirpath != NULL && libdirpath[0] != '!') {
d = opendir(libdirpath);
if (d != NULL) {
dfd = dirfd(d);
codesize += calculate_dir_size(dfd);
closedir(d);
}
}

/* compute asec size if it is given */
// 如果apk安装在外部存储上, 统计asecsize
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);
}
// 统计/data/user/<userid>/<packageName>下的文件大小, 计入codesize/cachesize/datasize
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);

/* most stuff in the pkgdir is data, except for the "cache"
* directory and below, which is cache, and the "lib" directory
* and below, which is code...
*/
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;
/* always skip "." and ".." */
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);
}
// 如为lib目录,统计目录大小, 计入codesize
if(!strcmp(name,"lib")) {
codesize += dirsize + statsize;
// 如为cache目录,统计目录大小, 计入cachesize
} else if(!strcmp(name,"cache")) {
cachesize += dirsize + statsize;
// 如为其他目录,统计目录大小, 计入datasize
} else {
datasize += dirsize + statsize;
}
// lib目录且为链接, 这部分很小, 可以忽略不计
} else if (de->d_type == DT_LNK && !strcmp(name,"lib")) {
// This is the symbolic link to the application's library
// code. We'll count this as code instead of data, since
// it is not something that the app creates.
if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) {
codesize += stat_size(&s);
}
} else {
// 非目录, 普通文件, 计入datasize
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;
// 内卡作主卡, codeSize dataSize + externalCodeSize + externalDataSize
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;
/* SPRD:438010 Monkey test NullPointerException@{*/
if (mAppSize != null) {
mAppSize.setText(getSizeStr(codeSize));
}
/* @}*/
}
if (mLastDataSize != dataSize) {
mLastDataSize = dataSize;
mDataSize.setText(getSizeStr(dataSize));
}
long cacheSize = mAppEntry.cacheSize + mAppEntry.externalCacheSize;
/* SPRD:fix bug 393783 , "let 12.00 KB does not dispaly @{*/
if (cacheSize > 0) {
cacheSize = cacheSize - 12 * 1024;
}
/* @} */
if (mLastCacheSize != cacheSize) {
mLastCacheSize = cacheSize;
mCacheSize.setText(getSizeStr(cacheSize));
}
if (mLastTotalSize != mAppEntry.size) {
mLastTotalSize = mAppEntry.size;
// mTotalSize 的大小.
mTotalSize.setText(getSizeStr(mAppEntry.size));
}
}
}
// 上述逻辑与6.0 7.0相同.

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) {
// stats.(externalCodeSize + externalObbSize + externalDataSize + externalMediaSize + codesize + datasize)
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;
}

};
// 调用函数仍然为 getPackageSizeInfo.

// 下面的流程也与 6.0 7.0相同.
void handleStartCopy() throws RemoteException {
synchronized (mInstallLock) {
mSuccess = getPackageSizeInfoLI(mStats.packageName, mStats.userHandle, mStats);
}

if (mSuccess) {
final boolean mounted;
if (Environment.isExternalStorageEmulated()) {
mounted = true;
} else {
/* SPRD: support double sdcard
* chanage interface, getExternalStorageState->getExternalStoragePathState
*/
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));

// Always subtract cache size, since it's a subdirectory
mStats.externalDataSize -= mStats.externalCacheSize;

mStats.externalMediaSize = calculateDirectorySize(mContainerService,
userEnv.buildExternalStorageAppMediaDirs(mStats.packageName));

mStats.externalObbSize = calculateDirectorySize(mContainerService,
userEnv.buildExternalStorageAppObbDirs(mStats.packageName));
}
}

// 展开 PackageManager的 getPackageSizeInfoLI 函数:
// 该函数与6.0的一致/
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;
}
/* SPRD: support double sdcard
* Add support for install apk to internal sdcard @{
* @orig
* if (p != null && (isExternal(p) || isForwardLocked(p))) {
*/
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();
}
}
// TODO: extend to measure size of split APKs
String[] dexCodeInstructionSets = getDexCodeInstructionSets(getAppDexInstructionSets(ps));
int res = mInstaller.getSizeInfo(packageName, userHandle, p.baseCodePath, libDirRoot,
publicSrcDir, asecPath, dexCodeInstructionSets, pStats);
if (res < 0) {
return false;
}

// Fix-up for forward-locked applications in ASEC containers.
if (!isExternal(p) && !isInternalSd(p)) {
/* @} */
pStats.codeSize += pStats.externalCodeSize;
pStats.externalCodeSize = 0L;
}

return true;
}
// 查看installd的get_size代码也与6.0的相同, 下面不再写了.

综上, 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(); // SPRD: chanage interface
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));

// Always subtract cache size, since it's a subdirectory
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;
}
/* SPRD: Add support for install apk to internal sdcard @{ */
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;
}

// Fix-up for forward-locked applications in ASEC containers.
/* SPRD: Add support for install apk to internal sdcard @{ */
if (!isExternal(p) && !isInternalSd(p)) {
/* @} */
pStats.codeSize += pStats.externalCodeSize;
pStats.externalCodeSize = 0L;
}

return true;
}
// installd中的统计方式与5.0相同

综上, Android4.4 与 Android5.0 / Android6.0的统计方式是一致的, 不再赘述.