apk installLocationがpreferExternalに設定されてインストールに失敗しました
5178 ワード
お客様のフィードバックでapk installLocationをpreferExternalに設定した後、sdcardパスがいっぱいになった場合、内部dataパーティションに自動的にインストールされるのではなく、インストールに失敗します.
installLocationはapkがインストール場所を指定するための属性で、preferExternalに設定するとプログラムがsdcardに優先的にインストールされ、sdcardの容量が不足するとdataパーティションにインストールされます.システムでは、本当にインストール場所を指定する変数はrecommendedInstallLocationです.recommendedInstallLocationの値は、recommendAppInstallLocation関数の戻り値です.
recommendAppInstallLocation関数でdataとsdcardの2つのインストールパーティションの使用可能な容量を判断します.まずdataパーティションの判断
そしてsdcardの利用可能容量を判断する
dataパーティションの手順と大きく異なり、fitsOnSdはsdcardパーティションが使用可能かどうかのフラグです.isUnderExternalThreshold関数による具体的な判断
問題は上のisUnderExternalThreshold関数で、この関数の手順は次のとおりです.
1、sizeMbはインストールするapkファイルの自身のサイズと依存するlibライブラリのサイズの和であり、その値は最小2である.
2、availSdMbはsdcardパーティションに残っている空きスペースの大きさで、ここでの計算はちょっと問題がある
ここのコードを修正すると
installLocationはapkがインストール場所を指定するための属性で、preferExternalに設定するとプログラムがsdcardに優先的にインストールされ、sdcardの容量が不足するとdataパーティションにインストールされます.システムでは、本当にインストール場所を指定する変数はrecommendedInstallLocationです.recommendedInstallLocationの値は、recommendAppInstallLocation関数の戻り値です.
public PackageInfoLite getMinimalPackageInfo(final String packagePath, int flags,
long threshold) {
...
ret.recommendedInstallLocation = recommendAppInstallLocation(pkg.installLocation,
packagePath, flags, threshold);
return ret;
}
recommendAppInstallLocation関数でdataとsdcardの2つのインストールパーティションの使用可能な容量を判断します.まずdataパーティションの判断
boolean fitsOnInternal = false;
if (checkBoth || prefer == PREFER_INTERNAL) {
try {
fitsOnInternal = isUnderInternalThreshold(apkFile, isForwardLocked, threshold);
} catch (IOException e) {
return PackageHelper.RECOMMEND_FAILED_INVALID_URI;
}
}
fitsOnInternalはdataパーティションが利用可能かどうかのフラグであり、isUnderInternalThreshold関数で具体的な利用可能容量の判断を行うprivate boolean isUnderInternalThreshold(File apkFile, boolean isForwardLocked, long threshold)
throws IOException {
long size = apkFile.length();
if (size == 0 && !apkFile.exists()) {
throw new FileNotFoundException();
}
if (isForwardLocked) {
size += PackageHelper.extractPublicFiles(apkFile.getAbsolutePath(), null);
}
final StatFs internalStats = new StatFs(Environment.getDataDirectory().getPath());
final long availInternalSize = (long) internalStats.getAvailableBlocks()
* (long) internalStats.getBlockSize();
return (availInternalSize - size) > threshold;
}
そしてsdcardの利用可能容量を判断する
boolean fitsOnSd = false;
if (!emulated && (checkBoth || prefer == PREFER_EXTERNAL)) {
try {
fitsOnSd = isUnderExternalThreshold(apkFile, isForwardLocked);
} catch (IOException e) {
return PackageHelper.RECOMMEND_FAILED_INVALID_URI;
}
}
dataパーティションの手順と大きく異なり、fitsOnSdはsdcardパーティションが使用可能かどうかのフラグです.isUnderExternalThreshold関数による具体的な判断
private boolean isUnderExternalThreshold(File apkFile, boolean isForwardLocked)
throws IOException {
if (Environment.isExternalStorageEmulated()) {
return false;
}
final int sizeMb = calculateContainerSize(apkFile, isForwardLocked);
final int availSdMb;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
final StatFs sdStats = new StatFs(Environment.getExternalStorageDirectory().getPath());
final int blocksToMb = (1 << 20) / sdStats.getBlockSize();
availSdMb = sdStats.getAvailableBlocks() * blocksToMb;
} else {
availSdMb = -1;
}
return availSdMb > sizeMb;
}
問題は上のisUnderExternalThreshold関数で、この関数の手順は次のとおりです.
1、sizeMbはインストールするapkファイルの自身のサイズと依存するlibライブラリのサイズの和であり、その値は最小2である.
private int calculateContainerSize(File apkFile, boolean forwardLocked) throws IOException {
// Calculate size of container needed to hold base APK.
long sizeBytes = apkFile.length();
if (sizeBytes == 0 && !apkFile.exists()) {
throw new FileNotFoundException();
}
// Check all the native files that need to be copied and add that to the
// container size.
sizeBytes += NativeLibraryHelper.sumNativeBinariesLI(apkFile);
if (forwardLocked) {
sizeBytes += PackageHelper.extractPublicFiles(apkFile.getPath(), null);
}
int sizeMb = (int) (sizeBytes >> 20);
if ((sizeBytes - (sizeMb * 1024 * 1024)) > 0) {
sizeMb++;
}
/*
* Add buffer size because we don't have a good way to determine the
* real FAT size. Your FAT size varies with how many directory entries
* you need, how big the whole filesystem is, and other such headaches.
*/
sizeMb++;
return sizeMb;
}
2、availSdMbはsdcardパーティションに残っている空きスペースの大きさで、ここでの計算はちょっと問題がある
final StatFs sdStats = new StatFs(Environment.getExternalStorageDirectory().getPath());
final int blocksToMb = (1 << 20) / sdStats.getBlockSize();
availSdMb = sdStats.getAvailableBlocks() * blocksToMb;
blocksToMbは1 mbが何ブロックあるかを示し、sdStats.getavailableBlocks()はsdcardの利用可能なブロックの数を返すので、正しいavailSdMb計算式はsdStatsであるべきである.getAvailableBlocks()/blocksToMb ここのコードを修正すると
final int blocksToMb = (1 << 20) / sdStats.getBlockSize();
availSdMb = sdStats.getAvailableBlocks() / blocksToMb;
でOKです.