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.
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 68373cb..ff16c6e 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -600,7 +600,7 @@
             } else if (opt.equals("-t")) {
                 installFlags |= PackageManager.INSTALL_ALLOW_TEST;
             } else if (opt.equals("-s")) {
-                installFlags |= PackageManager.INSTALL_ON_SDCARD;
+                installFlags |= PackageManager.INSTALL_EXTERNAL;
             } else {
                 System.err.println("Error: Unknown option: " + opt);
                 showUsage();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index b4fe698..db6a4bf 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2659,102 +2659,6 @@
             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);
-
-        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 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 = 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(mContext.getContentResolver(),
-                        Settings.System.SET_INSTALL_LOCATION, 0) != 0;
-                if (setInstallLoc) {
-                    // Pick user preference
-                    int installPreference = Settings.System.getInt(mContext.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 ? INSTALL_ON_SDCARD : INSTALL_FAILED_INSUFFICIENT_STORAGE;
-                } else if (installOnlyInternal){
-                    // Check on internal flash
-                    return fitsOnInt ? INSTALL_ON_INTERNAL_FLASH : INSTALL_FAILED_INSUFFICIENT_STORAGE;
-                }
-            }
-            // Try to install internally
-            if (fitsOnInt) {
-                return INSTALL_ON_INTERNAL_FLASH;
-            }
-            // Try the sdcard now.
-            if (fitsOnSd) {
-                return INSTALL_ON_SDCARD;
-            }
-            // Return error code
-            return INSTALL_FAILED_INSUFFICIENT_STORAGE;
-        }
-
         private final ContextImpl mContext;
         private final IPackageManager mPM;
 
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 17bee48..ff2ed3d 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -258,14 +258,7 @@
      * package has to be installed on the sdcard.
      * @hide
      */
-    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;
+    public static final int INSTALL_EXTERNAL = 0x00000008;
 
     /**
      * Flag parameter for
@@ -529,6 +522,14 @@
     public static final int INSTALL_PARSE_FAILED_MANIFEST_EMPTY = -109;
 
     /**
+     * Installation failed return code: this is passed to the {@link IPackageInstallObserver} by
+     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
+     * if the system failed to install the package because of system issues.
+     * @hide
+     */
+    public static final int INSTALL_FAILED_INTERNAL_ERROR = -110;
+
+    /**
      * Indicates the state of installation. Used by PackageManager to
      * figure out incomplete installations. Say a package is being installed
      * (the state is set to PKG_INSTALL_INCOMPLETE) and remains so till
@@ -627,23 +628,6 @@
      */
     public static final String ACTION_CLEAN_EXTERNAL_STORAGE
             = "android.content.pm.CLEAN_EXTERNAL_STORAGE";
-    
-    /**
-     * Determines best place to install an application: either SD or internal FLASH.
-     * If applications explicitly set installLocation in their manifest, that
-     * preference takes precedence. If not a recommended location is returned
-     * based on current available storage on internal flash or sdcard.
-     * @param pkgInfo PackageParser.Package of the package that is to be installed.
-     * Call utility method to obtain.
-     * @return {@link INSTALL_ON_INTERNAL_FLASH} if it is best to install package on internal
-     * storage, {@link INSTALL_ON_SDCARD} if it is best to install package on SD card,
-     * and {@link INSTALL_FAILED_INSUFFICIENT_STORAGE} if insufficient space to safely install
-     * the application. {@link INSTALL_PARSE_FAILED_NOT_APK} Is returned if any input
-     * parameter is <code>null</code>.
-     * This recommendation does take into account the package's own flags.
-     * @hide
-     */
-    public abstract int recommendAppInstallLocation(PackageParser.Package pkg);
 
     /**
      * Retrieve overall information about an application package that is
diff --git a/core/java/com/android/internal/app/IMediaContainerService.aidl b/core/java/com/android/internal/app/IMediaContainerService.aidl
index 726e28f..c0e9587 100755
--- a/core/java/com/android/internal/app/IMediaContainerService.aidl
+++ b/core/java/com/android/internal/app/IMediaContainerService.aidl
@@ -25,4 +25,5 @@
                 String key, String resFileName);
     boolean copyResource(in Uri packageURI,
                 in ParcelFileDescriptor outStream);
+    int getRecommendedInstallLocation(in Uri fileUri);
 }
\ No newline at end of file
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
new file mode 100644
index 0000000..4d7d2d1
--- /dev/null
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.content;
+
+/**
+ * Constants used internally between the PackageManager
+ * and media container service transports.
+ */
+public class PackageHelper {
+    public static final int RECOMMEND_INSTALL_INTERNAL = 1;
+    public static final int RECOMMEND_INSTALL_EXTERNAL = 2;
+    public static final int RECOMMEND_FAILED_INSUFFICIENT_STORAGE = -1;
+    public static final int RECOMMEND_FAILED_INVALID_APK = -2;
+}
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..8e030e50 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.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.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 @@
             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 @@
                 }
             }
         }
-        //Log.i(TAG, "Deleting: " + path);
         path.delete();
     }
     
@@ -341,4 +384,104 @@
         }
         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;
+    }
+
 }
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index a5213a0..812ff64 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -20,6 +20,7 @@
 import com.android.internal.app.ResolverActivity;
 import com.android.common.FastXmlSerializer;
 import com.android.common.XmlUtils;
+import com.android.internal.content.PackageHelper;
 import com.android.server.JournaledFile;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -304,6 +305,7 @@
     static final int INIT_COPY = 5;
     static final int MCS_UNBIND = 6;
     static final int START_CLEANING_PACKAGE = 7;
+    static final int FIND_INSTALL_LOC = 8;
     // Delay time in millisecs
     static final int BROADCAST_DELAY = 10 * 1000;
     private ServiceConnection mDefContainerConn = new ServiceConnection() {
@@ -319,8 +321,8 @@
     };
 
     class PackageHandler extends Handler {
-        final ArrayList<InstallArgs> mPendingInstalls =
-            new ArrayList<InstallArgs>();
+        final ArrayList<InstallParams> mPendingInstalls =
+            new ArrayList<InstallParams>();
         // Service Connection to remote media container service to copy
         // package uri's from external media onto secure containers
         // or internal storage.
@@ -332,21 +334,20 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case INIT_COPY: {
-                    InstallArgs args = (InstallArgs) msg.obj;
-                    args.createCopyFile();
+                    InstallParams params = (InstallParams) msg.obj;
                     Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
                     if (mContainerService != null) {
                         // No need to add to pending list. Use remote stub directly
-                        handleStartCopy(args);
+                        handleStartCopy(params);
                     } else {
                         if (mContext.bindService(service, mDefContainerConn,
                                 Context.BIND_AUTO_CREATE)) {
-                            mPendingInstalls.add(args);
+                            mPendingInstalls.add(params);
                         } else {
                             Log.e(TAG, "Failed to bind to media container service");
                             // Indicate install failure TODO add new error code
-                            processPendingInstall(args,
-                                    PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE);
+                            processPendingInstall(createInstallArgs(params),
+                                    PackageManager.INSTALL_FAILED_INTERNAL_ERROR);
                         }
                     }
                     break;
@@ -357,9 +358,9 @@
                         mContainerService = (IMediaContainerService) msg.obj;
                     }
                     if (mPendingInstalls.size() > 0) {
-                        InstallArgs args = mPendingInstalls.remove(0);
-                        if (args != null) {
-                            handleStartCopy(args);
+                        InstallParams params = mPendingInstalls.remove(0);
+                        if (params != null) {
+                            handleStartCopy(params);
                         }
                     }
                     break;
@@ -423,22 +424,56 @@
 
         // Utility method to initiate copying apk via media
         // container service.
-        private void handleStartCopy(InstallArgs args) {
-            int ret = PackageManager.INSTALL_SUCCEEDED;
-            if (mContainerService == null) {
-                // Install error
-                ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
-            } else {
-                ret = args.copyApk(mContainerService);
+        private void handleStartCopy(InstallParams params) {
+            int ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+            if (mContainerService != null) {
+                // Remote call to find out default install location
+                int loc = params.getInstallLocation(mContainerService);
+                // Use install location to create InstallArgs and temporary
+                // install location
+                if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE){
+                    ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+                } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) {
+                    ret = PackageManager.INSTALL_FAILED_INVALID_APK;
+                } else {
+                    if ((params.flags & PackageManager.INSTALL_EXTERNAL) == 0){
+                        if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
+                            // Set the flag to install on external media.
+                            params.flags |= PackageManager.INSTALL_EXTERNAL;
+                        } else {
+                            // Make sure the flag for installing on external
+                            // media is unset
+                            params.flags &= ~PackageManager.INSTALL_EXTERNAL;
+                        }
+                    }
+                    // Disable forward locked apps on sdcard.
+                    if ((params.flags & PackageManager.INSTALL_FORWARD_LOCK) != 0 &&
+                            (params.flags & PackageManager.INSTALL_EXTERNAL) != 0) {
+                        // Make sure forward locked apps can only be installed
+                        // on internal storage
+                        Log.w(TAG, "Cannot install protected apps on sdcard");
+                        ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
+                    } else {
+                        ret = PackageManager.INSTALL_SUCCEEDED;
+                    }
+                }
             }
             mHandler.sendEmptyMessage(MCS_UNBIND);
+            // Create the file args now.
+            InstallArgs args = createInstallArgs(params);
+            if (ret == PackageManager.INSTALL_SUCCEEDED) {
+                // Create copy only if we are not in an erroneous state.
+                args.createCopyFile();
+                // Remote call to initiate copy
+                ret = args.copyApk(mContainerService);
+            }
             processPendingInstall(args, ret);
         }
     }
 
     static boolean installOnSd(int flags) {
         if (((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) ||
-                ((flags & PackageManager.INSTALL_ON_SDCARD) == 0)) {
+                ((flags & PackageManager.INSTALL_EXTERNAL) == 0)) {
             return false;
         }
         return true;
@@ -4249,29 +4284,11 @@
                 android.Manifest.permission.INSTALL_PACKAGES, null);
 
         Message msg = mHandler.obtainMessage(INIT_COPY);
-        msg.obj = createInstallArgs(packageURI, observer, flags, installerPackageName);
+        msg.obj = new InstallParams(packageURI, observer, flags,
+                installerPackageName);
         mHandler.sendMessage(msg);
     }
 
-    private InstallArgs createInstallArgs(Uri packageURI, IPackageInstallObserver observer,
-            int flags, String installerPackageName) {
-        if (installOnSd(flags)) {
-            return new SdInstallArgs(packageURI, observer, flags,
-                    installerPackageName);
-        } else {
-            return new FileInstallArgs(packageURI, observer, flags,
-                    installerPackageName);
-        }
-    }
-
-    private InstallArgs createInstallArgs(int flags, String fullCodePath, String fullResourcePath) {
-        if (installOnSd(flags)) {
-            return new SdInstallArgs(fullCodePath, fullResourcePath);
-        } else {
-            return new FileInstallArgs(fullCodePath, fullResourcePath);
-        }
-    }
-
     private void processPendingInstall(final InstallArgs args, final int currentStatus) {
         // Queue up an async operation since the package installation may take a little while.
         mHandler.post(new Runnable() {
@@ -4327,6 +4344,45 @@
         });
     }
 
+    static final class InstallParams {
+        final IPackageInstallObserver observer;
+        int flags;
+        final Uri packageURI;
+        final String installerPackageName;
+        InstallParams(Uri packageURI,
+                IPackageInstallObserver observer, int flags,
+                String installerPackageName) {
+            this.packageURI = packageURI;
+            this.flags = flags;
+            this.observer = observer;
+            this.installerPackageName = installerPackageName;
+        }
+
+        public int getInstallLocation(IMediaContainerService imcs) {
+            try {
+                return imcs.getRecommendedInstallLocation(packageURI);
+            } catch (RemoteException e) {
+            }
+            return  -1;
+        }
+    };
+
+    private InstallArgs createInstallArgs(InstallParams params) {
+        if (installOnSd(params.flags)) {
+            return new SdInstallArgs(params);
+        } else {
+            return new FileInstallArgs(params);
+        }
+    }
+
+    private InstallArgs createInstallArgs(int flags, String fullCodePath, String fullResourcePath) {
+        if (installOnSd(flags)) {
+            return new SdInstallArgs(fullCodePath, fullResourcePath);
+        } else {
+            return new FileInstallArgs(fullCodePath, fullResourcePath);
+        }
+    }
+
     static abstract class InstallArgs {
         final IPackageInstallObserver observer;
         final int flags;
@@ -4359,10 +4415,9 @@
         String codeFileName;
         String resourceFileName;
 
-        FileInstallArgs(Uri packageURI,
-                IPackageInstallObserver observer, int flags,
-                String installerPackageName) {
-            super(packageURI, observer, flags, installerPackageName);
+        FileInstallArgs(InstallParams params) {
+            super(params.packageURI, params.observer,
+                    params.flags, params.installerPackageName);
         }
 
         FileInstallArgs(String fullCodePath, String fullResourcePath) {
@@ -4373,6 +4428,10 @@
             resourceFileName = fullResourcePath;
         }
 
+        String getCodePath() {
+            return codeFileName;
+        }
+
         void createCopyFile() {
             boolean fwdLocked = isFwdLocked(flags);
             installDir = fwdLocked ? mDrmAppPrivateInstallDir : mAppInstallDir;
@@ -4380,10 +4439,6 @@
             resourceFileName = getResourcePathFromCodePath();
         }
 
-        String getCodePath() {
-            return codeFileName;
-        }
-
         int copyApk(IMediaContainerService imcs) {
             // Get a ParcelFileDescriptor to write to the output file
             File codeFile = new File(codeFileName);
@@ -4528,10 +4583,9 @@
         String cachePath;
         static final String RES_FILE_NAME = "pkg.apk";
 
-        SdInstallArgs(Uri packageURI,
-                IPackageInstallObserver observer, int flags,
-                String installerPackageName) {
-           super(packageURI, observer, flags, installerPackageName);
+        SdInstallArgs(InstallParams params) {
+            super(params.packageURI, params.observer,
+                    params.flags, params.installerPackageName);
         }
 
         SdInstallArgs(String fullCodePath, String fullResourcePath) {
@@ -5105,7 +5159,7 @@
         String installerPackageName = args.installerPackageName;
         File tmpPackageFile = new File(args.getCodePath());
         boolean forwardLocked = ((pFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
-        boolean onSd = ((pFlags & PackageManager.INSTALL_ON_SDCARD) != 0);
+        boolean onSd = ((pFlags & PackageManager.INSTALL_EXTERNAL) != 0);
         boolean replace = false;
         int scanMode = SCAN_MONITOR | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE
                 | (newInstall ? SCAN_NEW_INSTALL : 0);
@@ -5136,14 +5190,6 @@
             res.returnCode = pp.getParseError();
             return;
         }
-        // Some preinstall checks
-        if (forwardLocked && onSd) {
-            // Make sure forward locked apps can only be installed
-            // on internal storage
-            Log.w(TAG, "Cannot install protected apps on sdcard");
-            res.returnCode = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
-            return;
-        }
         // Get rid of all references to package scan path via parser.
         pp = null;
         String oldCodePath = null;
@@ -5560,7 +5606,7 @@
         if (deleteCodeAndResources) {
             // TODO can pick up from PackageSettings as well
             int installFlags = ((p.applicationInfo.flags & ApplicationInfo.FLAG_ON_SDCARD)!=0) ?
-                    PackageManager.INSTALL_ON_SDCARD : 0;
+                    PackageManager.INSTALL_EXTERNAL : 0;
             installFlags |= ((p.applicationInfo.flags & ApplicationInfo.FLAG_FORWARD_LOCK)!=0) ?
                     PackageManager.INSTALL_FORWARD_LOCK : 0;
             outInfo.args = createInstallArgs(installFlags,
diff --git a/test-runner/android/test/mock/MockPackageManager.java b/test-runner/android/test/mock/MockPackageManager.java
index 2a4b3f7..f1ba44a 100644
--- a/test-runner/android/test/mock/MockPackageManager.java
+++ b/test-runner/android/test/mock/MockPackageManager.java
@@ -449,12 +449,4 @@
     public boolean isSafeMode() {
         throw new UnsupportedOperationException();
     }
-
-    /**
-     * @hide
-     */
-    @Override
-    public int recommendAppInstallLocation(PackageParser.Package pkg) {
-        throw new UnsupportedOperationException();
-    }
 }
diff --git a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
index 07bd489..8f4d0a1 100755
--- a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
@@ -192,6 +192,27 @@
         }
     }
 
+    public boolean invokeInstallPackageFail(Uri packageURI, int flags,
+            final String pkgName, int result) throws Exception {
+        PackageInstallObserver observer = new PackageInstallObserver();
+        try {
+            // Wait on observer
+            synchronized(observer) {
+                getPm().installPackage(packageURI, observer, flags, null);
+                long waitTime = 0;
+                while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
+                    observer.wait(WAIT_TIME_INCR);
+                    waitTime += WAIT_TIME_INCR;
+                }
+                if(!observer.isDone()) {
+                    throw new Exception("Timed out waiting for packageInstalled callback");
+                }
+                return (observer.returnCode == result);
+            }
+        } finally {
+        }
+    }
+
     Uri getInstallablePackage(int fileResId, File outFile) {
         Resources res = mContext.getResources();
         InputStream is = null;
@@ -238,7 +259,7 @@
             assertEquals(publicSrcPath, appInstallPath);
         } else {
             assertFalse((info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0);
-            if ((flags & PackageManager.INSTALL_ON_SDCARD) != 0) {
+            if ((flags & PackageManager.INSTALL_EXTERNAL) != 0) {
                 assertTrue((info.flags & ApplicationInfo.FLAG_ON_SDCARD) != 0);
                 // Hardcoded for now
                 assertTrue(srcPath.startsWith("/asec"));
@@ -253,6 +274,13 @@
             failStr("failed with exception : " + e);
         }
     }
+    private void assertNotInstalled(String pkgName) {
+        try {
+            ApplicationInfo info = getPm().getApplicationInfo(pkgName, 0);
+            fail(pkgName + " shouldnt be installed");
+        } catch (NameNotFoundException e) {
+        }
+    }
 
     class InstallParams {
         String outFileName;
@@ -265,30 +293,42 @@
         }
     }
 
+    private InstallParams sampleInstallFromRawResource(int flags, boolean cleanUp) {
+        return installFromRawResource("install.apk", R.raw.install, flags, cleanUp,
+                false, -1);
+    }
     /*
      * Utility function that reads a apk bundled as a raw resource
      * copies it into own data directory and invokes
      * PackageManager api to install it.
      */
-    public InstallParams installFromRawResource(int flags, boolean cleanUp) {
-        String outFileName = "install.apk";
+    private InstallParams installFromRawResource(String outFileName,
+            int rawResId, int flags, boolean cleanUp, boolean fail, int result) {
         File filesDir = mContext.getFilesDir();
         File outFile = new File(filesDir, outFileName);
-        Uri packageURI = getInstallablePackage(R.raw.install, outFile);
+        Uri packageURI = getInstallablePackage(rawResId, outFile);
         PackageParser.Package pkg = parsePackage(packageURI);
         assertNotNull(pkg);
-        InstallReceiver receiver = new InstallReceiver(pkg.packageName);
         InstallParams ip = null;
         try {
             try {
-                assertTrue(invokeInstallPackage(packageURI, flags,
-                        pkg.packageName, receiver));
+                if (fail) {
+                    // Make sure it doesn't exist
+                    getPm().deletePackage(pkg.packageName, null, 0);
+                    assertTrue(invokeInstallPackageFail(packageURI, flags,
+                            pkg.packageName, result));
+                    assertNotInstalled(pkg.packageName);
+                } else {
+                    InstallReceiver receiver = new InstallReceiver(pkg.packageName);
+                    assertTrue(invokeInstallPackage(packageURI, flags,
+                            pkg.packageName, receiver));
+                    // Verify installed information
+                    assertInstall(pkg.packageName, flags);
+                    ip = new InstallParams(pkg, outFileName, packageURI);
+                }
             } catch (Exception e) {
                 failStr("Failed with exception : " + e);
             }
-            // Verify installed information
-            assertInstall(pkg.packageName, flags);
-            ip = new InstallParams(pkg, outFileName, packageURI);
             return ip;
         } finally {
             if (cleanUp) {
@@ -299,17 +339,17 @@
 
     @MediumTest
     public void testInstallNormalInternal() {
-        installFromRawResource(0, true);
+        sampleInstallFromRawResource(0, true);
     }
 
     @MediumTest
     public void testInstallFwdLockedInternal() {
-        installFromRawResource(PackageManager.INSTALL_FORWARD_LOCK, true);
+        sampleInstallFromRawResource(PackageManager.INSTALL_FORWARD_LOCK, true);
     }
 
     @MediumTest
     public void testInstallSdcard() {
-        installFromRawResource(PackageManager.INSTALL_ON_SDCARD, true);
+        sampleInstallFromRawResource(PackageManager.INSTALL_EXTERNAL, true);
     }
 
     /* ------------------------- Test replacing packages --------------*/
@@ -373,7 +413,7 @@
      * again.
      */
     public void replaceFromRawResource(int flags) {
-        InstallParams ip = installFromRawResource(flags, false);
+        InstallParams ip = sampleInstallFromRawResource(flags, false);
         boolean replace = ((flags & PackageManager.INSTALL_REPLACE_EXISTING) != 0);
         GenericReceiver receiver;
         if (replace) {
@@ -409,7 +449,7 @@
 
     @MediumTest
     public void testReplaceFailSdcard() {
-        replaceFromRawResource(PackageManager.INSTALL_ON_SDCARD);
+        replaceFromRawResource(PackageManager.INSTALL_EXTERNAL);
     }
 
     @MediumTest
@@ -426,7 +466,7 @@
     @MediumTest
     public void testReplaceSdcard() {
         replaceFromRawResource(PackageManager.INSTALL_REPLACE_EXISTING |
-                PackageManager.INSTALL_ON_SDCARD);
+                PackageManager.INSTALL_EXTERNAL);
     }
 
     /* -------------- Delete tests ---*/
@@ -508,7 +548,7 @@
     }
 
     public void deleteFromRawResource(int iFlags, int dFlags) {
-        InstallParams ip = installFromRawResource(iFlags, false);
+        InstallParams ip = sampleInstallFromRawResource(iFlags, false);
         boolean retainData = ((dFlags & PackageManager.DONT_DELETE_DATA) != 0);
         GenericReceiver receiver = new DeleteReceiver(ip.pkg.packageName);
         DeleteObserver observer = new DeleteObserver();
@@ -550,7 +590,7 @@
 
     @MediumTest
     public void testDeleteSdcard() {
-        deleteFromRawResource(PackageManager.INSTALL_ON_SDCARD, 0);
+        deleteFromRawResource(PackageManager.INSTALL_EXTERNAL, 0);
     }
 
     @MediumTest
@@ -565,7 +605,7 @@
 
     @MediumTest
     public void testDeleteSdcardRetainData() {
-        deleteFromRawResource(PackageManager.INSTALL_ON_SDCARD, PackageManager.DONT_DELETE_DATA);
+        deleteFromRawResource(PackageManager.INSTALL_EXTERNAL, PackageManager.DONT_DELETE_DATA);
     }
 
     /* sdcard mount/unmount tests ******/
@@ -696,7 +736,7 @@
 
     private boolean mountFromRawResource() {
         // Install pkg on sdcard
-        InstallParams ip = installFromRawResource(PackageManager.INSTALL_ON_SDCARD |
+        InstallParams ip = sampleInstallFromRawResource(PackageManager.INSTALL_EXTERNAL |
                 PackageManager.INSTALL_REPLACE_EXISTING, false);
         if (localLOGV) Log.i(TAG, "Installed pkg on sdcard");
         boolean origState = getMediaState();
@@ -764,169 +804,32 @@
         }
     }
 
-    public void invokeRecommendAppInstallLocation(String outFileName,
-            int fileResId, int expected) {
-        int origSetting = Settings.System.getInt(mContext.getContentResolver(),
-                Settings.System.SET_INSTALL_LOCATION, 0);
-        try {
-            // Make sure the set install location setting is diabled.
-            Settings.System.putInt(mContext.getContentResolver(),
-                    Settings.System.SET_INSTALL_LOCATION, 0);
-            File filesDir = mContext.getFilesDir();
-            File outFile = new File(filesDir, outFileName);
-            Uri packageURI = getInstallablePackage(fileResId, outFile);
-            PackageParser.Package pkg = parsePackage(packageURI);
-            assertNotNull(pkg);
-            int installLoc = getPm().recommendAppInstallLocation(pkg);
-            Log.i(TAG, "expected=" + expected +", installLoc="+installLoc);
-            // Atleast one of the specified expected flags should be set.
-            boolean onFlash = (installLoc &
-                    PackageManager.INSTALL_ON_INTERNAL_FLASH) != 0;
-            boolean onSd = (installLoc &
-                    PackageManager.INSTALL_ON_SDCARD) != 0;
-            boolean expOnFlash = (expected &
-                    PackageManager.INSTALL_ON_INTERNAL_FLASH) != 0;
-            boolean expOnSd = (expected &
-                    PackageManager.INSTALL_ON_SDCARD) != 0;
-            assertTrue(expOnFlash == onFlash || expOnSd == onSd);
-        } finally {
-            // Restore original setting
-            Settings.System.putInt(mContext.getContentResolver(),
-                    Settings.System.SET_INSTALL_LOCATION, origSetting);
-        }
+    public void testManifestInstallLocationInternal() {
+        installFromRawResource("install.apk", R.raw.install_loc_internal,
+                0, true, false, -1);
     }
 
-    /*
-     * Tests if an apk can be installed on internal flash by
-     * explicitly specifying in its manifest.
-     */
-    public void testInstallLocationInternal() {
-        invokeRecommendAppInstallLocation("install.apk",
-                R.raw.install_loc_internal, PackageManager.INSTALL_ON_INTERNAL_FLASH);
+    public void testManifestInstallLocationSdcard() {
+        installFromRawResource("install.apk", R.raw.install_loc_sdcard,
+                PackageManager.INSTALL_EXTERNAL, true, false, -1);
     }
 
-    /*
-     * Tests if an apk can be installed on internal flash by
-     * explicitly specifying in its manifest and filling up
-     * internal flash. Should fail to install.
-     * TODO
-     */
-    public void xxxtestInstallLocationInternalFail() {
+    public void testManifestInstallLocationAuto() {
+        installFromRawResource("install.apk", R.raw.install_loc_auto,
+                0, true, false, -1);
     }
 
-    /*
-     * Tests if an apk can be installed on sdcard by
-     * explicitly specifying in its manifest.
-     */
-    public void testInstallLocationSdcard() {
-        // TODO No guarantee this will be on sdcard.
-        invokeRecommendAppInstallLocation("install.apk",
-                R.raw.install_loc_sdcard, PackageManager.INSTALL_ON_SDCARD
-                | PackageManager.INSTALL_ON_INTERNAL_FLASH);
+    public void testManifestInstallLocationUnspecified() {
+        installFromRawResource("install.apk", R.raw.install_loc_unspecified,
+                0, true, false, -1);
     }
 
-    /*
-     * Tests if an apk can be installed on sdcard by
-     * explicitly specifying in its manifest and filling up
-     * the sdcard. Should result in install failure
-     * TODO
-     */
-    public void xxxtestInstallLocationSdcardFail() {
+    public void testManifestInstallLocationFwdLockedSdcard() {
+        installFromRawResource("install.apk", R.raw.install_loc_sdcard,
+                PackageManager.INSTALL_FORWARD_LOCK |
+                PackageManager.INSTALL_EXTERNAL, true, true,
+                PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION);
     }
-
-    /*
-     * Tests if an apk can be installed by specifying
-     * auto for install location
-     */
-    public void xxxtestInstallLocationAutoInternal() {
-        // TODO clear and make room on internal flash
-        invokeRecommendAppInstallLocation("install.apk",
-                R.raw.install_loc_auto, PackageManager.INSTALL_ON_INTERNAL_FLASH);
-    }
-
-    /*
-     * Tests if an apk can be installed by specifying
-     * auto for install location
-     */
-    public void testInstallLocationAutoSdcard() {
-        // TODO clear and make room on sdcard.
-        // Fill up internal
-        invokeRecommendAppInstallLocation("install.apk",
-                R.raw.install_loc_auto, PackageManager.INSTALL_ON_SDCARD |
-                PackageManager.INSTALL_ON_INTERNAL_FLASH);
-    }
-
-    /*
-     * Tests if an apk can be installed by specifying
-     * auto for install location
-     * fill up both internal and sdcard
-     * TODO
-     */
-    public void xxxtestInstallLocationAutoFail() {
-    }
-    /*
-     * Tests where an apk gets installed based
-     * on a not specifying anything in manifest.
-     */
-    public void testInstallLocationUnspecifiedInt() {
-        invokeRecommendAppInstallLocation("install.apk",
-                R.raw.install_loc_unspecified, PackageManager.INSTALL_ON_INTERNAL_FLASH);
-    }
-
-    public void xxxtestInstallLocationUnspecifiedStorage() {
-        // TODO Fill up internal storage
-        invokeRecommendAppInstallLocation("install.apk",
-                R.raw.install_loc_unspecified, PackageManager.INSTALL_ON_SDCARD
-                | PackageManager.INSTALL_ON_INTERNAL_FLASH);
-    }
-
-    /*
-     * Tests where an apk gets installed by expcitly setting
-     * the user specified install location
-     */
-    public void testInstallLocationUserSpecifiedInternal() {
-        // Enable user setting
-        Settings.System.putInt(mContext.getContentResolver(),
-                Settings.System.SET_INSTALL_LOCATION, 1);
-        Settings.System.putInt(mContext.getContentResolver(),
-                Settings.System.DEFAULT_INSTALL_LOCATION, 1);
-        invokeRecommendAppInstallLocation("install.apk",
-                R.raw.install_loc_unspecified, PackageManager.INSTALL_ON_INTERNAL_FLASH);
-    }
-
-    /*
-     * Tests where an apk gets installed by expcitly setting
-     * the user specified install location
-     */
-    public void testInstallLocationUserSpecifiedSdcard() {
-        // Enable user setting
-        Settings.System.putInt(mContext.getContentResolver(),
-                Settings.System.SET_INSTALL_LOCATION, 1);
-        Settings.System.putInt(mContext.getContentResolver(),
-                Settings.System.DEFAULT_INSTALL_LOCATION, 2);
-        int i = Settings.System.getInt(mContext.getContentResolver(),
-                Settings.System.DEFAULT_INSTALL_LOCATION, 0);
-        invokeRecommendAppInstallLocation("install.apk",
-                R.raw.install_loc_unspecified, PackageManager.INSTALL_ON_SDCARD);
-        Settings.System.putInt(mContext.getContentResolver(),
-                Settings.System.SET_INSTALL_LOCATION, 0);
-    }
-    /*
-     * Tests where an apk gets installed by expcitly setting
-     * the user specified install location
-     */
-    public void testInstallLocationUserSpecifiedAuto() {
-        // Enable user setting
-        Settings.System.putInt(mContext.getContentResolver(),
-                Settings.System.SET_INSTALL_LOCATION, 1);
-        Settings.System.putInt(mContext.getContentResolver(),
-                Settings.System.DEFAULT_INSTALL_LOCATION, 0);
-        invokeRecommendAppInstallLocation("install.apk",
-                R.raw.install_loc_unspecified, PackageManager.INSTALL_ON_INTERNAL_FLASH);
-        Settings.System.putInt(mContext.getContentResolver(),
-                Settings.System.SET_INSTALL_LOCATION, 0);
-    }
-
     /*
      * TODO's
      * check version numbers for upgrades