summaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
authorSuchi Amalapurapu <asuchitra@google.com>2010-02-12 09:43:29 -0800
committerSuchi Amalapurapu <asuchitra@google.com>2010-02-12 18:50:47 -0800
commit5b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2 (patch)
treefc25f9f6adee005c287454a74837f6cbc1a072a0 /packages
parent70251b1704b9bb81443e7533d365400645dd5eaa (diff)
downloadframeworks_base-5b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2.zip
frameworks_base-5b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2.tar.gz
frameworks_base-5b993ce7bc29e43a3215a50ce6ce5d6550d4e5e2.tar.bz2
Include install location preference when installing packages.
Changes include Add new remote call in default container service to determine install location. Rename INSTALL_ON_SDCARD Remove recommentAppInstall method Add some additional flags used in remote stubs. Move check for protected apps prior to copy. Unit tests DefaultContainerService first parses the file uri(if content uri is specified it returns a default install internal only value) and returns a recommended location. Based on which the temporary id is determined either a file name or a container id and the file is copied there. This is then later renamed during install. Todo's light weight parsing of package when determining location since we just need the install location attribute only when finding out recomended location. This will also enable to move the check for updated system apps(cannot be on sdcard) prior to copying.
Diffstat (limited to 'packages')
-rwxr-xr-xpackages/DefaultContainerService/AndroidManifest.xml1
-rw-r--r--packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java147
2 files changed, 146 insertions, 2 deletions
diff --git a/packages/DefaultContainerService/AndroidManifest.xml b/packages/DefaultContainerService/AndroidManifest.xml
index 5ec72df..078daa7 100755
--- a/packages/DefaultContainerService/AndroidManifest.xml
+++ b/packages/DefaultContainerService/AndroidManifest.xml
@@ -6,6 +6,7 @@
<uses-permission android:name="android.permission.ASEC_DESTROY"/>
<uses-permission android:name="android.permission.ASEC_MOUNT_UNMOUNT"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM" />
<application android:label="@string/service_name">
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index c418ccb..8e030e5 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -1,10 +1,13 @@
package com.android.defcontainer;
import com.android.internal.app.IMediaContainerService;
-
+import com.android.internal.content.PackageHelper;
import android.content.Intent;
import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.Package;
import android.net.Uri;
import android.os.Debug;
import android.os.Environment;
@@ -15,8 +18,10 @@ import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.StatFs;
import android.app.IntentService;
import android.app.Service;
+import android.util.DisplayMetrics;
import android.util.Log;
import java.io.File;
@@ -28,6 +33,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import android.os.FileUtils;
+import android.provider.Settings;
/*
* This service copies a downloaded apk to a file passed in as
@@ -79,6 +85,44 @@ public class DefaultContainerService extends IntentService {
autoOut = new ParcelFileDescriptor.AutoCloseOutputStream(outStream);
return copyFile(packageURI, autoOut);
}
+
+ /*
+ * Determine the recommended install location for package
+ * specified by file uri location.
+ * @param fileUri the uri of resource to be copied. Should be a
+ * file uri
+ * @return Returns
+ * PackageHelper.RECOMMEND_INSTALL_INTERNAL to install on internal storage
+ * PackageHelper.RECOMMEND_INSTALL_EXTERNAL to install on external media
+ * PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE for storage errors
+ * PackageHelper.RECOMMEND_FAILED_INVALID_APK for parse errors.
+ */
+ public int getRecommendedInstallLocation(final Uri fileUri) {
+ if (!fileUri.getScheme().equals("file")) {
+ Log.w(TAG, "Falling back to installing on internal storage only");
+ return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ }
+ final String archiveFilePath = fileUri.getPath();
+ PackageParser packageParser = new PackageParser(archiveFilePath);
+ File sourceFile = new File(archiveFilePath);
+ DisplayMetrics metrics = new DisplayMetrics();
+ metrics.setToDefaults();
+ PackageParser.Package pkg = packageParser.parsePackage(sourceFile, archiveFilePath, metrics, 0);
+ if (pkg == null) {
+ Log.w(TAG, "Failed to parse package");
+ return PackageHelper.RECOMMEND_FAILED_INVALID_APK;
+ }
+ int loc = recommendAppInstallLocation(pkg);
+ if (loc == PackageManager.INSTALL_EXTERNAL) {
+ return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
+ } else if (loc == ERR_LOC) {
+ Log.i(TAG, "Failed to install insufficient storage");
+ return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
+ } else {
+ // Implies install on internal storage.
+ return 0;
+ }
+ }
};
public DefaultContainerService() {
@@ -111,7 +155,6 @@ public class DefaultContainerService extends IntentService {
}
}
}
- //Log.i(TAG, "Deleting: " + path);
path.delete();
}
@@ -341,4 +384,104 @@ 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;
+
+ public int recommendAppInstallLocation(Package pkg) {
+ // Initial implementation:
+ // 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%
+
+ if (pkg == null) {
+ return ERR_LOC;
+ }
+
+ StatFs internalFlashStats = new StatFs(Environment.getDataDirectory().getPath());
+ StatFs sdcardStats = new StatFs(Environment.getExternalStorageDirectory().getPath());
+
+ long totalInternalFlashSize = (long)internalFlashStats.getBlockCount() *
+ (long)internalFlashStats.getBlockSize();
+ long availInternalFlashSize = (long)internalFlashStats.getAvailableBlocks() *
+ (long)internalFlashStats.getBlockSize();
+ long availSDSize = (long)sdcardStats.getAvailableBlocks() *
+ (long)sdcardStats.getBlockSize();
+
+ double pctNandFree = (double)availInternalFlashSize / (double)totalInternalFlashSize;
+
+ final String archiveFilePath = pkg.mScanPath;
+ File apkFile = new File(archiveFilePath);
+ long pkgLen = apkFile.length();
+
+ boolean auto = true;
+ // To make final copy
+ long reqInstallSize = pkgLen;
+ // For dex files
+ long reqInternalSize = 1 * pkgLen;
+ boolean intThresholdOk = (pctNandFree >= LOW_NAND_FLASH_TRESHOLD);
+ boolean intAvailOk = ((reqInstallSize + reqInternalSize) < availInternalFlashSize);
+ boolean fitsOnSd = (reqInstallSize < availSDSize) && intThresholdOk &&
+ (reqInternalSize < availInternalFlashSize);
+ boolean fitsOnInt = intThresholdOk && intAvailOk;
+
+ // Consider application flags preferences as well...
+ boolean installOnlyOnSd = (pkg.installLocation ==
+ PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
+ boolean installOnlyInternal = (pkg.installLocation ==
+ PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+ if (installOnlyInternal) {
+ // If set explicitly in manifest,
+ // let that override everything else
+ auto = false;
+ } else if (installOnlyOnSd){
+ // Check if this can be accommodated on the sdcard
+ if (fitsOnSd) {
+ auto = false;
+ }
+ } else {
+ // Check if user option is enabled
+ boolean setInstallLoc = Settings.System.getInt(getApplicationContext()
+ .getContentResolver(),
+ Settings.System.SET_INSTALL_LOCATION, 0) != 0;
+ if (setInstallLoc) {
+ // Pick user preference
+ int installPreference = Settings.System.getInt(getApplicationContext()
+ .getContentResolver(),
+ Settings.System.DEFAULT_INSTALL_LOCATION,
+ PackageInfo.INSTALL_LOCATION_AUTO);
+ if (installPreference == 1) {
+ installOnlyInternal = true;
+ auto = false;
+ } else if (installPreference == 2) {
+ installOnlyOnSd = true;
+ auto = false;
+ }
+ }
+ }
+ if (!auto) {
+ if (installOnlyOnSd) {
+ return fitsOnSd ? PackageManager.INSTALL_EXTERNAL : ERR_LOC;
+ } else if (installOnlyInternal){
+ // Check on internal flash
+ return fitsOnInt ? 0 : ERR_LOC;
+ }
+ }
+ // Try to install internally
+ if (fitsOnInt) {
+ return 0;
+ }
+ // Try the sdcard now.
+ if (fitsOnSd) {
+ return PackageManager.INSTALL_EXTERNAL;
+ }
+ // Return error code
+ return ERR_LOC;
+ }
+
}