summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKenny Root <kroot@google.com>2011-03-15 14:48:56 -0700
committerAndroid (Google) Code Review <android-gerrit@google.com>2011-03-15 14:48:56 -0700
commitd022c23aed0d022057c0074a158b522c83e906e4 (patch)
treef3ea5813b838aca632dfbaa4cf627f72625b25de
parent0e5e2e2b2bb3d6c3a0216181df29e6bfdc71256f (diff)
parent62e1b4e9d41a01db423b5e4684ecf529ed46106d (diff)
downloadframeworks_base-d022c23aed0d022057c0074a158b522c83e906e4.zip
frameworks_base-d022c23aed0d022057c0074a158b522c83e906e4.tar.gz
frameworks_base-d022c23aed0d022057c0074a158b522c83e906e4.tar.bz2
Merge "Revise free space checks for package installs"
-rwxr-xr-xcore/java/com/android/internal/app/IMediaContainerService.aidl5
-rw-r--r--core/java/com/android/internal/content/PackageHelper.java11
-rw-r--r--packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java309
-rw-r--r--services/java/com/android/server/DeviceStorageMonitorService.java20
-rw-r--r--services/java/com/android/server/PackageManagerService.java34
5 files changed, 220 insertions, 159 deletions
diff --git a/core/java/com/android/internal/app/IMediaContainerService.aidl b/core/java/com/android/internal/app/IMediaContainerService.aidl
index aee1626..dd22e25 100755
--- a/core/java/com/android/internal/app/IMediaContainerService.aidl
+++ b/core/java/com/android/internal/app/IMediaContainerService.aidl
@@ -27,8 +27,9 @@ interface IMediaContainerService {
String key, String resFileName);
boolean copyResource(in Uri packageURI,
in ParcelFileDescriptor outStream);
- PackageInfoLite getMinimalPackageInfo(in Uri fileUri, int flags);
- boolean checkFreeStorage(boolean external, in Uri fileUri);
+ PackageInfoLite getMinimalPackageInfo(in Uri fileUri, in int flags, in long threshold);
+ boolean checkInternalFreeStorage(in Uri fileUri, in long threshold);
+ boolean checkExternalFreeStorage(in Uri fileUri);
ObbInfo getObbInfo(in String filename);
long calculateDirectorySize(in String directory);
}
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index d6c43f9..b57046c 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -56,18 +56,13 @@ public class PackageHelper {
return null;
}
- public static String createSdDir(long sizeBytes, String cid,
+ public static String createSdDir(int sizeMb, String cid,
String sdEncKey, int uid) {
// Create mount point via MountService
IMountService mountService = getMountService();
- int sizeMb = (int) (sizeBytes >> 20);
- if ((sizeBytes - (sizeMb * 1024 * 1024)) > 0) {
- sizeMb++;
- }
- // Add buffer size
- sizeMb++;
+
if (localLOGV)
- Log.i(TAG, "Size of container " + sizeMb + " MB " + sizeBytes + " bytes");
+ Log.i(TAG, "Size of container " + sizeMb + " MB");
try {
int rc = mountService.createSecureContainer(
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 21f77e3..15c1653 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -47,7 +47,7 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.util.LinkedList;
+import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
@@ -117,7 +117,7 @@ public class DefaultContainerService extends IntentService {
* @return Returns PackageInfoLite object containing
* the package info and recommended app location.
*/
- public PackageInfoLite getMinimalPackageInfo(final Uri fileUri, int flags) {
+ public PackageInfoLite getMinimalPackageInfo(final Uri fileUri, int flags, long threshold) {
PackageInfoLite ret = new PackageInfoLite();
if (fileUri == null) {
Log.i(TAG, "Invalid package uri " + fileUri);
@@ -131,15 +131,9 @@ public class DefaultContainerService extends IntentService {
return ret;
}
String archiveFilePath = fileUri.getPath();
- PackageParser packageParser = new PackageParser(archiveFilePath);
- File sourceFile = new File(archiveFilePath);
DisplayMetrics metrics = new DisplayMetrics();
metrics.setToDefaults();
- PackageParser.PackageLite pkg = packageParser.parsePackageLite(
- archiveFilePath, 0);
- // Nuke the parser reference right away and force a gc
- packageParser = null;
- Runtime.getRuntime().gc();
+ PackageParser.PackageLite pkg = PackageParser.parsePackageLite(archiveFilePath, 0);
if (pkg == null) {
Log.w(TAG, "Failed to parse package");
ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
@@ -147,12 +141,22 @@ public class DefaultContainerService extends IntentService {
}
ret.packageName = pkg.packageName;
ret.installLocation = pkg.installLocation;
- ret.recommendedInstallLocation = recommendAppInstallLocation(pkg.installLocation, archiveFilePath, flags);
+ ret.recommendedInstallLocation = recommendAppInstallLocation(pkg.installLocation,
+ archiveFilePath, flags, threshold);
return ret;
}
- public boolean checkFreeStorage(boolean external, Uri fileUri) {
- return checkFreeStorageInner(external, fileUri);
+ @Override
+ public boolean checkInternalFreeStorage(Uri packageUri, long threshold)
+ throws RemoteException {
+ final File apkFile = new File(packageUri.getPath());
+ return isUnderInternalThreshold(apkFile, threshold);
+ }
+
+ @Override
+ public boolean checkExternalFreeStorage(Uri packageUri) throws RemoteException {
+ final File apkFile = new File(packageUri.getPath());
+ return isUnderExternalThreshold(apkFile);
}
public ObbInfo getObbInfo(String filename) {
@@ -225,41 +229,15 @@ public class DefaultContainerService extends IntentService {
String codePath = packageURI.getPath();
File codeFile = new File(codePath);
- // Calculate size of container needed to hold base APK.
- long sizeBytes = codeFile.length();
-
- // Check all the native files that need to be copied and add that to the container size.
- ZipFile zipFile;
- List<Pair<ZipEntry, String>> nativeFiles;
- try {
- zipFile = new ZipFile(codeFile);
-
- nativeFiles = new LinkedList<Pair<ZipEntry, String>>();
-
- NativeLibraryHelper.listPackageNativeBinariesLI(zipFile, nativeFiles);
+ // Native files we need to copy to the container.
+ List<Pair<ZipEntry, String>> nativeFiles = new ArrayList<Pair<ZipEntry, String>>();
- final int N = nativeFiles.size();
- for (int i = 0; i < N; i++) {
- final Pair<ZipEntry, String> entry = nativeFiles.get(i);
-
- /*
- * Note that PackageHelper.createSdDir adds a 1MB padding on
- * our claimed size, so we don't have to worry about block
- * alignment here.
- */
- sizeBytes += entry.first.getSize();
- }
- } catch (ZipException e) {
- Log.w(TAG, "Failed to extract data from package file", e);
- return null;
- } catch (IOException e) {
- Log.w(TAG, "Failed to cache package shared libs", e);
- return null;
- }
+ // Calculate size of container needed to hold base APK.
+ final int sizeMb = calculateContainerSize(codeFile, nativeFiles);
// Create new container
String newCachePath = null;
- if ((newCachePath = PackageHelper.createSdDir(sizeBytes, newCid, key, Process.myUid())) == null) {
+ if ((newCachePath = PackageHelper.createSdDir(sizeMb, newCid, key, Process.myUid())) == null) {
Log.e(TAG, "Failed to create container " + newCid);
return null;
}
@@ -274,6 +252,8 @@ public class DefaultContainerService extends IntentService {
}
try {
+ ZipFile zipFile = new ZipFile(codeFile);
+
File sharedLibraryDir = new File(newCachePath, LIB_DIR_NAME);
sharedLibraryDir.mkdir();
@@ -386,159 +366,196 @@ public class DefaultContainerService extends IntentService {
return true;
}
- // Constants related to app heuristics
- // No-installation limit for internal flash: 10% or less space available
- private static final double LOW_NAND_FLASH_TRESHOLD = 0.1;
+ private static final int PREFER_INTERNAL = 1;
+ private static final int PREFER_EXTERNAL = 2;
- // SD-to-internal app size threshold: currently set to 1 MB
- private static final long INSTALL_ON_SD_THRESHOLD = (1024 * 1024);
- private static final int ERR_LOC = -1;
-
- private int recommendAppInstallLocation(int installLocation,
- String archiveFilePath, int flags) {
- boolean checkInt = false;
- boolean checkExt = false;
+ private int recommendAppInstallLocation(int installLocation, String archiveFilePath, int flags,
+ long threshold) {
+ int prefer;
boolean checkBoth = false;
+
check_inner : {
- // Check flags.
+ /*
+ * Explicit install flags should override the manifest settings.
+ */
if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) {
- // Check for forward locked app
- checkInt = true;
+ /*
+ * Forward-locked applications cannot be installed on SD card,
+ * so only allow checking internal storage.
+ */
+ prefer = PREFER_INTERNAL;
break check_inner;
} else if ((flags & PackageManager.INSTALL_INTERNAL) != 0) {
- // Explicit flag to install internally.
- // Check internal storage and return
- checkInt = true;
+ prefer = PREFER_INTERNAL;
break check_inner;
} else if ((flags & PackageManager.INSTALL_EXTERNAL) != 0) {
- // Explicit flag to install externally.
- // Check external storage and return
- checkExt = true;
+ prefer = PREFER_EXTERNAL;
break check_inner;
}
- // Check for manifest option
+
+ /* No install flags. Check for manifest option. */
if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
- checkInt = true;
+ prefer = PREFER_INTERNAL;
break check_inner;
} else if (installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
- checkExt = true;
+ prefer = PREFER_EXTERNAL;
checkBoth = true;
break check_inner;
} else if (installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
- checkInt = true;
+ // We default to preferring internal storage.
+ prefer = PREFER_INTERNAL;
checkBoth = true;
break check_inner;
}
+
// Pick user preference
int installPreference = Settings.System.getInt(getApplicationContext()
.getContentResolver(),
Settings.Secure.DEFAULT_INSTALL_LOCATION,
PackageHelper.APP_INSTALL_AUTO);
if (installPreference == PackageHelper.APP_INSTALL_INTERNAL) {
- checkInt = true;
+ prefer = PREFER_INTERNAL;
break check_inner;
} else if (installPreference == PackageHelper.APP_INSTALL_EXTERNAL) {
- checkExt = true;
+ prefer = PREFER_EXTERNAL;
break check_inner;
}
- // Fall back to default policy if nothing else is specified.
- checkInt = true;
+
+ /*
+ * Fall back to default policy of internal-only if nothing else is
+ * specified.
+ */
+ prefer = PREFER_INTERNAL;
}
- // Package size = code size + cache size + data size
- // If code size > 1 MB, install on SD card.
- // Else install on internal NAND flash, unless space on NAND is less than 10%
- String status = Environment.getExternalStorageState();
- long availSDSize = -1;
- boolean mediaAvailable = false;
- if (!Environment.isExternalStorageEmulated() && status.equals(Environment.MEDIA_MOUNTED)) {
- StatFs sdStats = new StatFs(
- Environment.getExternalStorageDirectory().getPath());
- availSDSize = (long)sdStats.getAvailableBlocks() *
- (long)sdStats.getBlockSize();
- mediaAvailable = true;
+ final boolean emulated = Environment.isExternalStorageEmulated();
+
+ final File apkFile = new File(archiveFilePath);
+
+ boolean fitsOnInternal = false;
+ if (checkBoth || prefer == PREFER_INTERNAL) {
+ fitsOnInternal = isUnderInternalThreshold(apkFile, threshold);
}
- StatFs internalStats = new StatFs(Environment.getDataDirectory().getPath());
- long totalInternalSize = (long)internalStats.getBlockCount() *
- (long)internalStats.getBlockSize();
- long availInternalSize = (long)internalStats.getAvailableBlocks() *
- (long)internalStats.getBlockSize();
-
- double pctNandFree = (double)availInternalSize / (double)totalInternalSize;
-
- File apkFile = new File(archiveFilePath);
- long pkgLen = apkFile.length();
-
- // To make final copy
- long reqInstallSize = pkgLen;
- // For dex files. Just ignore and fail when extracting. Max limit of 2Gig for now.
- long reqInternalSize = 0;
- boolean intThresholdOk = (pctNandFree >= LOW_NAND_FLASH_TRESHOLD);
- boolean intAvailOk = ((reqInstallSize + reqInternalSize) < availInternalSize);
+
boolean fitsOnSd = false;
- if (mediaAvailable && (reqInstallSize < availSDSize)) {
- // If we do not have an internal size requirement
- // don't do a threshold check.
- if (reqInternalSize == 0) {
- fitsOnSd = true;
- } else if ((reqInternalSize < availInternalSize) && intThresholdOk) {
- fitsOnSd = true;
- }
+ if (!emulated && (checkBoth || prefer == PREFER_EXTERNAL)) {
+ fitsOnSd = isUnderExternalThreshold(apkFile);
}
- boolean fitsOnInt = intThresholdOk && intAvailOk;
- if (checkInt) {
- // Check for internal memory availability
- if (fitsOnInt) {
+
+ if (prefer == PREFER_INTERNAL) {
+ if (fitsOnInternal) {
return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
}
- } else if (checkExt) {
+ } else if (!emulated && prefer == PREFER_EXTERNAL) {
if (fitsOnSd) {
return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
}
}
+
if (checkBoth) {
- // Check for internal first
- if (fitsOnInt) {
+ if (fitsOnInternal) {
return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
- }
- // Check for external next
- if (fitsOnSd) {
+ } else if (!emulated && fitsOnSd) {
return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
}
}
- if ((checkExt || checkBoth) && !mediaAvailable) {
+
+ /*
+ * If they requested to be on the external media by default, return that
+ * the media was unavailable. Otherwise, indicate there was insufficient
+ * storage space available.
+ */
+ if (!emulated && (checkBoth || prefer == PREFER_EXTERNAL)
+ && !Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
return PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE;
+ } else {
+ return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
}
- return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
}
- private boolean checkFreeStorageInner(boolean external, Uri packageURI) {
- File apkFile = new File(packageURI.getPath());
- long size = apkFile.length();
- if (external) {
- String status = Environment.getExternalStorageState();
- long availSDSize = -1;
- if (status.equals(Environment.MEDIA_MOUNTED)) {
- StatFs sdStats = new StatFs(
- Environment.getExternalStorageDirectory().getPath());
- availSDSize = (long)sdStats.getAvailableBlocks() *
- (long)sdStats.getBlockSize();
+ private boolean isUnderInternalThreshold(File apkFile, long threshold) {
+ final long size = apkFile.length();
+
+ final StatFs internalStats = new StatFs(Environment.getDataDirectory().getPath());
+ final long availInternalSize = (long) internalStats.getAvailableBlocks()
+ * (long) internalStats.getBlockSize();
+
+ return (availInternalSize - size) > threshold;
+ }
+
+
+ private boolean isUnderExternalThreshold(File apkFile) {
+ if (Environment.isExternalStorageEmulated()) {
+ return false;
+ }
+
+ final int sizeMb = calculateContainerSize(apkFile, null);
+
+ final int availSdMb;
+ if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
+ StatFs sdStats = new StatFs(Environment.getExternalStorageDirectory().getPath());
+ long availSdSize = (long) (sdStats.getAvailableBlocks() * sdStats.getBlockSize());
+ availSdMb = (int) (availSdSize >> 20);
+ } else {
+ availSdMb = -1;
+ }
+
+ return availSdMb > sizeMb;
+ }
+
+ /**
+ * Calculate the container size for an APK. Takes into account the
+ *
+ * @param apkFile file from which to calculate size
+ * @return size in megabytes (2^20 bytes)
+ */
+ private int calculateContainerSize(File apkFile, List<Pair<ZipEntry, String>> outFiles) {
+ // Calculate size of container needed to hold base APK.
+ long sizeBytes = apkFile.length();
+
+ // Check all the native files that need to be copied and add that to the
+ // container size.
+ ZipFile zipFile;
+ final List<Pair<ZipEntry, String>> nativeFiles;
+ try {
+ zipFile = new ZipFile(apkFile);
+
+ if (outFiles != null) {
+ nativeFiles = outFiles;
+ } else {
+ nativeFiles = new ArrayList<Pair<ZipEntry, String>>();
+ }
+
+ NativeLibraryHelper.listPackageNativeBinariesLI(zipFile, nativeFiles);
+
+ final int N = nativeFiles.size();
+ for (int i = 0; i < N; i++) {
+ final Pair<ZipEntry, String> entry = nativeFiles.get(i);
+
+ /*
+ * Note a 1MB padding is added to the claimed size, so we don't
+ * have to worry about block alignment here.
+ */
+ sizeBytes += entry.first.getSize();
}
- return availSDSize > size;
+ } catch (ZipException e) {
+ Log.w(TAG, "Failed to extract data from package file", e);
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to cache package shared libs", e);
}
- StatFs internalStats = new StatFs(Environment.getDataDirectory().getPath());
- long totalInternalSize = (long)internalStats.getBlockCount() *
- (long)internalStats.getBlockSize();
- long availInternalSize = (long)internalStats.getAvailableBlocks() *
- (long)internalStats.getBlockSize();
-
- double pctNandFree = (double)availInternalSize / (double)totalInternalSize;
- // To make final copy
- long reqInstallSize = size;
- // For dex files. Just ignore and fail when extracting. Max limit of 2Gig for now.
- long reqInternalSize = 0;
- boolean intThresholdOk = (pctNandFree >= LOW_NAND_FLASH_TRESHOLD);
- boolean intAvailOk = ((reqInstallSize + reqInternalSize) < availInternalSize);
- return intThresholdOk && intAvailOk;
+
+ 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;
}
}
diff --git a/services/java/com/android/server/DeviceStorageMonitorService.java b/services/java/com/android/server/DeviceStorageMonitorService.java
index 0fba7c3..b0d2158 100644
--- a/services/java/com/android/server/DeviceStorageMonitorService.java
+++ b/services/java/com/android/server/DeviceStorageMonitorService.java
@@ -398,4 +398,24 @@ class DeviceStorageMonitorService extends Binder {
// force an early check
postCheckMemoryMsg(true, 0);
}
+
+ /**
+ * Callable from other things in the system service to obtain the low memory
+ * threshold.
+ *
+ * @return low memory threshold in bytes
+ */
+ public long getMemoryLowThreshold() {
+ return mMemLowThreshold;
+ }
+
+ /**
+ * Callable from other things in the system process to check whether memory
+ * is low.
+ *
+ * @return true is memory is low
+ */
+ public boolean isMemoryLow() {
+ return mLowMemFlag;
+ }
}
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 04f271d..3aebb5c 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -4919,12 +4919,24 @@ class PackageManagerService extends IPackageManager.Stub {
Slog.w(TAG, "Cannot install fwd locked apps on sdcard");
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else {
+ final long lowThreshold;
+
+ final DeviceStorageMonitorService dsm = (DeviceStorageMonitorService) ServiceManager
+ .getService(DeviceStorageMonitorService.SERVICE);
+ if (dsm == null) {
+ Log.w(TAG, "Couldn't get low memory threshold; no free limit imposed");
+ lowThreshold = 0L;
+ } else {
+ lowThreshold = dsm.getMemoryLowThreshold();
+ }
+
// Remote call to find out default install location
final PackageInfoLite pkgLite;
try {
mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
Intent.FLAG_GRANT_READ_URI_PERMISSION);
- pkgLite = mContainerService.getMinimalPackageInfo(packageURI, flags);
+ pkgLite = mContainerService.getMinimalPackageInfo(packageURI, flags,
+ lowThreshold);
} finally {
mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
@@ -5144,10 +5156,26 @@ class PackageManagerService extends IPackageManager.Stub {
}
boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException {
+ final long lowThreshold;
+
+ final DeviceStorageMonitorService dsm = (DeviceStorageMonitorService) ServiceManager
+ .getService(DeviceStorageMonitorService.SERVICE);
+ if (dsm == null) {
+ Log.w(TAG, "Couldn't get low memory threshold; no free limit imposed");
+ lowThreshold = 0L;
+ } else {
+ if (dsm.isMemoryLow()) {
+ Log.w(TAG, "Memory is reported as being too low; aborting package install");
+ return false;
+ }
+
+ lowThreshold = dsm.getMemoryLowThreshold();
+ }
+
try {
mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
Intent.FLAG_GRANT_READ_URI_PERMISSION);
- return imcs.checkFreeStorage(false, packageURI);
+ return imcs.checkInternalFreeStorage(packageURI, lowThreshold);
} finally {
mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
@@ -5373,7 +5401,7 @@ class PackageManagerService extends IPackageManager.Stub {
try {
mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
Intent.FLAG_GRANT_READ_URI_PERMISSION);
- return imcs.checkFreeStorage(true, packageURI);
+ return imcs.checkExternalFreeStorage(packageURI);
} finally {
mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
}