Merge "Apps on SD card project. Refactored recommendAppInstallLocation(..) method in PackageManager by making it an instance method. Since PackageManager has only abstarct instance methods, moved implementation to ApplicationContext.ApplicationPackageManager class, in line with the rest of the method implementations. Tah way, chage is consistent with best coding practices. Also MockPackageManager received the additional method."
diff --git a/core/java/android/app/ApplicationContext.java b/core/java/android/app/ApplicationContext.java
index fe05393..6c65bd8 100644
--- a/core/java/android/app/ApplicationContext.java
+++ b/core/java/android/app/ApplicationContext.java
@@ -71,6 +71,7 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.DropBoxManager;
+import android.os.Environment;
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.IBinder;
@@ -80,6 +81,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.StatFs;
 import android.os.Vibrator;
 import android.os.FileUtils.FileStatus;
 import android.telephony.TelephonyManager;
@@ -2537,6 +2539,76 @@
             return PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
         }
 
+        // 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);
+
+        @Override
+        public int recommendAppInstallLocation(ApplicationInfo appInfo, Uri packageURI) {
+            // 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 ((packageURI == null) || (appInfo == null)) {
+                return INSTALL_PARSE_FAILED_NOT_APK;
+            }
+
+            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 = packageURI.getPath();
+            File apkFile = new File(archiveFilePath);
+            long pkgLen = apkFile.length();
+
+            // Consider application flags preferences as well...
+            boolean installOnlyOnSD = ((appInfo.flags & PackageManager.INSTALL_ON_SDCARD) != 0);
+
+            // These are not very precise measures, but I guess it is hard to estimate sizes
+            // before installing the package.
+            // As a shortcut, I am assuming that the package fits on NAND flash if the available
+            // space is three times that of the APK size. For SD, we only worry about the APK size.
+            // Since packages are downloaded into SD, this might not even be necessary.
+            boolean fitsOnSD = (pkgLen < availSDSize) && ((2 * pkgLen) < availInternalFlashSize);
+            boolean fitsOnInternalFlash = ((pkgLen * 3) < availInternalFlashSize);
+
+            // Does not fit, recommend no installation.
+            if (!fitsOnSD && !fitsOnInternalFlash) {
+                return INSTALL_FAILED_INSUFFICIENT_STORAGE;
+            }
+
+            if (pkgLen < (INSTALL_ON_SD_THRESHOLD) && fitsOnInternalFlash && !(installOnlyOnSD)) {
+                // recommend internal NAND likely
+                if (pctNandFree < LOW_NAND_FLASH_TRESHOLD) {
+                    // Low space on NAND (<10%) - install on SD
+                    return INSTALL_ON_SDCARD;
+                }
+                return INSTALL_ON_INTERNAL_FLASH;
+            } else {
+                if (fitsOnSD) {
+                    // Recommend SD card
+                    return INSTALL_ON_SDCARD;
+                } else if (fitsOnInternalFlash && (pctNandFree >= LOW_NAND_FLASH_TRESHOLD) &&
+                        !(installOnlyOnSD)) {
+                    return INSTALL_ON_INTERNAL_FLASH;
+                } else {
+                    return INSTALL_FAILED_INSUFFICIENT_STORAGE;
+                }
+            }
+        }
+
         private final ApplicationContext mContext;
         private final IPackageManager mPM;
 
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index d10c8f8..1e45f17 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -261,6 +261,13 @@
     public static final int INSTALL_ON_SDCARD = 0x00000008;
 
     /**
+     * Convenience flag parameter to indicate that this package has to be installed
+     * on internal flash.
+     * @hide
+     */
+    public static final int INSTALL_ON_INTERNAL_FLASH = 0x00000000;
+
+    /**
      * Flag parameter for
      * {@link #setComponentEnabledSetting(android.content.ComponentName, int, int)} to indicate
      * that you don't want to kill the app containing the component.  Be careful when you set this
@@ -605,89 +612,22 @@
     @SdkConstant(SdkConstantType.FEATURE)
     public static final String FEATURE_LIVE_WALLPAPER = "android.software.live_wallpaper";
 
-    // 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 INSTALL_ON_INTERNAL_FLASH = 0;
-
     /**
      * Determines best place to install an application: either SD or internal FLASH.
      * Tweak the algorithm for best results.
-     * @param tmpPackageFile APK file containing the application to install.
-     * @return <code>PKG_INSTALL_INTERNAL</code> if it is best to install package on internal
-     * storage, <code>PKG_INSTALL_ON_SD</code> if it is best to install package on SD card,
-     * and <code>PKG_CANNOT_FIT</code> if insufficient space to safely install the app.
-     * This response does not take into account the package's own flags.
+     * @param appInfo ApplicationInfo object og the package to install.
+     * Call utility method to obtain.
+     * @param packageURI URI identifying the package's APK file.
+     * @return <code>INSTALL_ON_INTERNAL_FLASH</code> if it is best to install package on internal
+     * storage, <code>INSTALL_ON_SDCARD</code> if it is best to install package on SD card,
+     * and <code>INSTALL_FAILED_INSUFFICIENT_STORAGE</code> if insufficient space to safely install
+     * the application. <code>INSTALL_PARSE_FAILED_NOT_APK</code> Is returned if any input
+     * parameter is <code>null</code>.
+     * This recommendation does take into account the package's own flags.
      * @hide
      */
-    public static int recommendAppInstallLocation(ApplicationInfo appInfo, Uri packageURI) {
-        // 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 5%
-        // 0 = install on internal FLASH
-        // 1 = install on SD card
-        // (-1) = insufficient space - package cannot be installed.
+    public abstract int recommendAppInstallLocation(ApplicationInfo appInfo, Uri packageURI);
 
-        if ((packageURI == null) || (appInfo == null)) {
-            return (-1);
-        }
-
-        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 = packageURI.getPath();
-        File apkFile = new File(archiveFilePath);
-        long pkgLen = apkFile.length();
-
-        // Consider application flags preferences as well...
-        boolean installOnlyOnSD = ((appInfo.flags & PackageManager.INSTALL_ON_SDCARD) != 0);
-
-        // These are not very precise measures, but I guess it is hard to estimate sizes
-        // before installing the package.
-        // As a shortcut, I am assuming that the package fits on NAND flash if the available
-        // space is three times that of the APK size. For SD, we only worry about the APK size.
-        // Since packages are downloaded into SD, this might not even be necessary.
-        boolean fitsOnSD = (pkgLen < availSDSize) && ((2 * pkgLen) < availInternalFlashSize);
-        boolean fitsOnInternalFlash = ((pkgLen * 3) < availInternalFlashSize);
-
-        // Does not fit, recommend no installation.
-        if (!fitsOnSD && !fitsOnInternalFlash) {
-            return (-1);
-        }
-
-        if (pkgLen < (INSTALL_ON_SD_THRESHOLD) && fitsOnInternalFlash && !(installOnlyOnSD)) {
-            // recommend internal NAND likely
-            if (pctNandFree < LOW_NAND_FLASH_TRESHOLD) {
-                // Low space on NAND (<10%) - install on SD
-                return INSTALL_ON_SDCARD;
-            }
-            return INSTALL_ON_INTERNAL_FLASH;
-        } else {
-            if (fitsOnSD) {
-                // Recommend SD card
-                return INSTALL_ON_SDCARD;
-            } else if (fitsOnInternalFlash && (pctNandFree >= LOW_NAND_FLASH_TRESHOLD) &&
-                    !(installOnlyOnSD)) {
-                return INSTALL_ON_INTERNAL_FLASH;
-            } else {
-                return (-1);
-            }
-        }
-    }
-    
     /**
      * Retrieve overall information about an application package that is
      * installed on the system.
diff --git a/test-runner/android/test/mock/MockPackageManager.java b/test-runner/android/test/mock/MockPackageManager.java
index 2f313af..cbe0253 100644
--- a/test-runner/android/test/mock/MockPackageManager.java
+++ b/test-runner/android/test/mock/MockPackageManager.java
@@ -438,4 +438,12 @@
     public boolean isSafeMode() {
         throw new UnsupportedOperationException();
     }
+
+    /**
+     * @hide
+     */
+    @Override
+    public int recommendAppInstallLocation(ApplicationInfo appInfo, Uri packageURI) {
+        throw new UnsupportedOperationException();
+    }
 }