Loading core/java/com/android/internal/app/IMediaContainerService.aidl +3 −2 Original line number Diff line number Diff line Loading @@ -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); } core/java/com/android/internal/content/PackageHelper.java +3 −8 Original line number Diff line number Diff line Loading @@ -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( Loading packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java +163 −146 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading @@ -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; Loading @@ -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) { Loading Loading @@ -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); final int N = nativeFiles.size(); for (int i = 0; i < N; i++) { final Pair<ZipEntry, String> entry = nativeFiles.get(i); // Native files we need to copy to the container. List<Pair<ZipEntry, String>> nativeFiles = new ArrayList<Pair<ZipEntry, String>>(); /* * 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; } Loading @@ -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(); Loading Loading @@ -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; // 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 static final int PREFER_INTERNAL = 1; private static final int PREFER_EXTERNAL = 2; 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; } final boolean emulated = Environment.isExternalStorageEmulated(); final File apkFile = new File(archiveFilePath); boolean fitsOnInternal = false; if (checkBoth || prefer == PREFER_INTERNAL) { fitsOnInternal = isUnderInternalThreshold(apkFile, threshold); } // 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; } 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; } } boolean fitsOnInt = intThresholdOk && intAvailOk; if (checkInt) { // Check for internal memory availability if (fitsOnInt) { if (!emulated && (checkBoth || prefer == PREFER_EXTERNAL)) { fitsOnSd = isUnderExternalThreshold(apkFile); } 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; } } 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(); } return availSDSize > size; } 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; 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(); } } 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); } 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; } } services/java/com/android/server/DeviceStorageMonitorService.java +20 −0 Original line number Diff line number Diff line Loading @@ -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; } } services/java/com/android/server/PackageManagerService.java +31 −3 Original line number Diff line number Diff line Loading @@ -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); } Loading Loading @@ -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); } Loading Loading @@ -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); } Loading Loading
core/java/com/android/internal/app/IMediaContainerService.aidl +3 −2 Original line number Diff line number Diff line Loading @@ -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); }
core/java/com/android/internal/content/PackageHelper.java +3 −8 Original line number Diff line number Diff line Loading @@ -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( Loading
packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java +163 −146 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading @@ -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; Loading @@ -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) { Loading Loading @@ -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); final int N = nativeFiles.size(); for (int i = 0; i < N; i++) { final Pair<ZipEntry, String> entry = nativeFiles.get(i); // Native files we need to copy to the container. List<Pair<ZipEntry, String>> nativeFiles = new ArrayList<Pair<ZipEntry, String>>(); /* * 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; } Loading @@ -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(); Loading Loading @@ -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; // 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 static final int PREFER_INTERNAL = 1; private static final int PREFER_EXTERNAL = 2; 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; } final boolean emulated = Environment.isExternalStorageEmulated(); final File apkFile = new File(archiveFilePath); boolean fitsOnInternal = false; if (checkBoth || prefer == PREFER_INTERNAL) { fitsOnInternal = isUnderInternalThreshold(apkFile, threshold); } // 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; } 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; } } boolean fitsOnInt = intThresholdOk && intAvailOk; if (checkInt) { // Check for internal memory availability if (fitsOnInt) { if (!emulated && (checkBoth || prefer == PREFER_EXTERNAL)) { fitsOnSd = isUnderExternalThreshold(apkFile); } 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; } } 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(); } return availSDSize > size; } 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; 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(); } } 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); } 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; } }
services/java/com/android/server/DeviceStorageMonitorService.java +20 −0 Original line number Diff line number Diff line Loading @@ -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; } }
services/java/com/android/server/PackageManagerService.java +31 −3 Original line number Diff line number Diff line Loading @@ -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); } Loading Loading @@ -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); } Loading Loading @@ -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); } Loading