app install on sdcard. provide skeleton implementation
to install an app on sdcard, just resources.
Add new install path for /asec in installd.
ignore . when checking for apk path since the sdcard packages id'ed
by package name.
Add new -s option to adb shell pm
Refactor fwd locked from scanMode to ApplicationInfo.
Add new flag for sd install
Add new parse flags for fwd locking and installing on sdcard
New mock api's in PackageManagerService to invoke MountService api's. These
will be refactored again and so have been wrapped internally.
Some error codes in PackageManager
Changes in PackageManagerService to use mPath and mScanPath during installation
and switch to using PackageParser.Package.applicationInfo attributes for
source and public resource directories.
Some known issues that will be addressed later
 using system_uid for now. needs some tinkering with uid and packagesetting creation to use the actual app uid
 error handling from vold not very robust. ignoring lot of things for now
 sending a delayed destroy to delete packages. will revisit later
 revisit temp file creation later. just copy for now
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index dcae0c7..79bda74 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -218,14 +218,20 @@
 static int is_valid_apk_path(const char *path)
 {
     int len = strlen(APK_DIR_PREFIX);
+int nosubdircheck = 0;
     if (strncmp(path, APK_DIR_PREFIX, len)) {
         len = strlen(PROTECTED_DIR_PREFIX);
         if (strncmp(path, PROTECTED_DIR_PREFIX, len)) {
-            LOGE("invalid apk path '%s' (bad prefix)\n", path);
-            return 0;
+            len = strlen(SDCARD_DIR_PREFIX);
+            if (strncmp(path, SDCARD_DIR_PREFIX, len)) {
+                LOGE("invalid apk path '%s' (bad prefix)\n", path);
+                return 0;
+            } else {
+                nosubdircheck = 1;
+            }
         }
     }
-    if (strchr(path + len, '/')) {
+    if ((nosubdircheck != 1) && strchr(path + len, '/')) {
         LOGE("invalid apk path '%s' (subdir?)\n", path);
         return 0;
     }
diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h
index 1679d14..35a173e 100644
--- a/cmds/installd/installd.h
+++ b/cmds/installd/installd.h
@@ -68,6 +68,7 @@
 /* other handy constants */
 
 #define PROTECTED_DIR_PREFIX  "/data/app-private/"
+#define SDCARD_DIR_PREFIX  "/asec/"
 
 #define DALVIK_CACHE_PREFIX   "/data/dalvik-cache/"
 #define DALVIK_CACHE_POSTFIX  "/classes.dex"
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 79eb310b..4953f5d 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -594,6 +594,8 @@
                 }
             } else if (opt.equals("-t")) {
                 installFlags |= PackageManager.INSTALL_ALLOW_TEST;
+            } else if (opt.equals("-s")) {
+                installFlags |= PackageManager.INSTALL_ON_SDCARD;
             } else {
                 System.err.println("Error: Unknown option: " + opt);
                 showUsage();
@@ -822,7 +824,7 @@
         System.err.println("       pm list instrumentation [-f] [TARGET-PACKAGE]");
         System.err.println("       pm list features");
         System.err.println("       pm path PACKAGE");
-        System.err.println("       pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] PATH");
+        System.err.println("       pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] PATH");
         System.err.println("       pm uninstall [-k] PACKAGE");
         System.err.println("       pm enable PACKAGE_OR_COMPONENT");
         System.err.println("       pm disable PACKAGE_OR_COMPONENT");
@@ -854,6 +856,7 @@
         System.err.println("  -r: reinstall an exisiting app, keeping its data.");
         System.err.println("  -t: allow test .apks to be installed.");
         System.err.println("  -i: specify the installer package name.");
+        System.err.println("  -s: install package on sdcard.");
         System.err.println("");
         System.err.println("The uninstall command removes a package from the system. Options:");
         System.err.println("  -k: keep the data and cache directories around.");
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 3dea286..b27cd6c 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -218,6 +218,22 @@
     public static final int FLAG_NEVER_ENCRYPT = 1<<17;
 
     /**
+     * Value for {@link #flags}: Set to true if the application has been
+     * installed using the forward lock option.
+     *
+     * {@hide}
+     */
+    public static final int FLAG_FORWARD_LOCK = 1<<18;
+
+    /**
+     * Value for {@link #flags}: Set to true if the application is
+     * currently installed on the sdcard.
+     *
+     * {@hide}
+     */
+    public static final int FLAG_ON_SDCARD = 1<<19;
+
+    /**
      * Flags associated with the application.  Any combination of
      * {@link #FLAG_SYSTEM}, {@link #FLAG_DEBUGGABLE}, {@link #FLAG_HAS_CODE},
      * {@link #FLAG_PERSISTENT}, {@link #FLAG_FACTORY_TEST}, and
@@ -227,6 +243,8 @@
      * {@link #FLAG_SUPPORTS_NORMAL_SCREENS},
      * {@link #FLAG_SUPPORTS_LARGE_SCREENS}, {@link #FLAG_RESIZEABLE_FOR_SCREENS},
      * {@link #FLAG_SUPPORTS_SCREEN_DENSITIES}.
+     * {@link #FLAG_FWD_LOCKED},
+     * {@link #FLAG_ON_SDCARD}
      */
     public int flags = 0;
     
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index fc6538f..54db5e0 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -296,4 +296,13 @@
      * in the special development "no pre-dexopt" mode.
      */
     boolean performDexOpt(String packageName);
+
+    /**
+     * Update status of external media on the package manager to scan and
+     * install packages installed on the external media. Like say the
+     * MountService uses this to call into the package manager to update
+     * status of sdcard.
+     */
+    void updateExternalMediaStatus(boolean mounted);
+
 }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 53a966d..bc59c94 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -252,6 +252,13 @@
     public static final int INSTALL_ALLOW_TEST = 0x00000004;
 
     /**
+     * Flag parameter for {@link #installPackage} to indicate that this
+     * package has to be installed on the sdcard.
+     * @hide
+     */
+    public static final int INSTALL_ON_SDCARD = 0x00000008;
+
+    /**
      * 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
@@ -411,6 +418,15 @@
      */
     public static final int INSTALL_FAILED_MISSING_FEATURE = -17;
 
+    // ------ Errors related to sdcard
+    /**
+     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
+     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+     * a secure container mount point couldn't be accessed on external media.
+     * @hide
+     */
+    public static final int INSTALL_FAILED_CONTAINER_ERROR = -18;
+
     /**
      * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
      * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index ad99f54..8a5df32 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -327,16 +327,18 @@
         return null;
     }
 
-    public final static int PARSE_IS_SYSTEM = 0x0001;
-    public final static int PARSE_CHATTY = 0x0002;
-    public final static int PARSE_MUST_BE_APK = 0x0004;
-    public final static int PARSE_IGNORE_PROCESSES = 0x0008;
+    public final static int PARSE_IS_SYSTEM = 1<<0;
+    public final static int PARSE_CHATTY = 1<<1;
+    public final static int PARSE_MUST_BE_APK = 1<<2;
+    public final static int PARSE_IGNORE_PROCESSES = 1<<3;
+    public final static int PARSE_FORWARD_LOCK = 1<<4;
+    public final static int PARSE_ON_SDCARD = 1<<5;
 
     public int getParseError() {
         return mParseError;
     }
 
-    public Package parsePackage(File sourceFile, String destFileName,
+    public Package parsePackage(File sourceFile, String destCodePath,
             DisplayMetrics metrics, int flags) {
         mParseError = PackageManager.INSTALL_SUCCEEDED;
 
@@ -413,8 +415,11 @@
         parser.close();
         assmgr.close();
 
-        pkg.applicationInfo.sourceDir = destFileName;
-        pkg.applicationInfo.publicSourceDir = destFileName;
+        // Set code and resource paths
+        pkg.mPath = destCodePath;
+        pkg.mScanPath = mArchiveSourcePath;
+        //pkg.applicationInfo.sourceDir = destCodePath;
+        //pkg.applicationInfo.publicSourceDir = destRes;
         pkg.mSignatures = null;
 
         return pkg;
@@ -1369,6 +1374,14 @@
             }
         }
 
+        if ((flags & PARSE_FORWARD_LOCK) != 0) {
+            ai.flags |= ApplicationInfo.FLAG_FORWARD_LOCK;
+        }
+
+        if ((flags & PARSE_ON_SDCARD) != 0) {
+            ai.flags |= ApplicationInfo.FLAG_ON_SDCARD;
+        }
+
         if (sa.getBoolean(
                 com.android.internal.R.styleable.AndroidManifestApplication_debuggable,
                 false)) {
@@ -2530,10 +2543,6 @@
         // preferred up order.
         public int mPreferredOrder = 0;
 
-        // For use by package manager service to keep track of which apps
-        // have been installed with forward locking.
-        public boolean mForwardLocked;
-        
         // For use by the package manager to keep track of the path to the
         // file an app came from.
         public String mScanPath;
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 2b12268..4b9b366 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -135,8 +135,8 @@
     static final int SCAN_NO_DEX = 1<<1;
     static final int SCAN_FORCE_DEX = 1<<2;
     static final int SCAN_UPDATE_SIGNATURE = 1<<3;
-    static final int SCAN_FORWARD_LOCKED = 1<<4;
-    static final int SCAN_NEW_INSTALL = 1<<5;
+    static final int SCAN_NEW_INSTALL = 1<<4;
+    static final int SCAN_NO_PATHS = 1<<5;
 
     final HandlerThread mHandlerThread = new HandlerThread("PackageManager",
             Process.THREAD_PRIORITY_BACKGROUND);
@@ -281,8 +281,10 @@
     final HashMap<String, ArrayList<String>> mPendingBroadcasts
             = new HashMap<String, ArrayList<String>>();
     static final int SEND_PENDING_BROADCAST = 1;
+    static final int DESTROY_SD_CONTAINER = 2;
     // Delay time in millisecs
     static final int BROADCAST_DELAY = 10 * 1000;
+    static final int DESTROY_SD_CONTAINER_DELAY = 30 * 1000;
 
     class PackageHandler extends Handler {
         PackageHandler(Looper looper) {
@@ -290,6 +292,15 @@
         }
         public void handleMessage(Message msg) {
             switch (msg.what) {
+                case DESTROY_SD_CONTAINER:
+                    String pkgName = (String) msg.obj;
+                    if (pkgName != null) {
+                        // Too bad we cannot handle the errors from destroying the containers.
+                        if (!destroySdDir(pkgName)) {
+                            Log.e(TAG, "Failed to destroy container for pkg : " + pkgName);
+                        }
+                    }
+                    break;
                 case SEND_PENDING_BROADCAST : {
                     String packages[];
                     ArrayList components[];
@@ -562,12 +573,12 @@
                 mFrameworkDir.getPath(), OBSERVER_EVENTS, true);
             mFrameworkInstallObserver.startWatching();
             scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM,
-                    scanMode | SCAN_NO_DEX);
+                    scanMode | SCAN_NO_DEX | SCAN_NO_PATHS);
             mSystemAppDir = new File(Environment.getRootDirectory(), "app");
             mSystemInstallObserver = new AppDirObserver(
                 mSystemAppDir.getPath(), OBSERVER_EVENTS, true);
             mSystemInstallObserver.startWatching();
-            scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM, scanMode);
+            scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM, scanMode | SCAN_NO_PATHS);
             mAppInstallDir = new File(dataDir, "app");
             if (mInstaller == null) {
                 // Make sure these dirs exist, when we are running in
@@ -594,7 +605,7 @@
             mDrmAppInstallObserver = new AppDirObserver(
                 mDrmAppPrivateInstallDir.getPath(), OBSERVER_EVENTS, false);
             mDrmAppInstallObserver.startWatching();
-            scanDirLI(mDrmAppPrivateInstallDir, 0, scanMode | SCAN_FORWARD_LOCKED);
+            scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK, scanMode);
 
             EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
                     SystemClock.uptimeMillis());
@@ -1961,12 +1972,7 @@
         int i;
         for (i=0; i<files.length; i++) {
             File file = new File(dir, files[i]);
-            File resFile = file;
-            // Pick up the resource path from settings for fwd locked apps
-            if ((scanMode & SCAN_FORWARD_LOCKED) != 0) {
-                resFile = null;
-            }
-            PackageParser.Package pkg = scanPackageLI(file, file, resFile,
+            PackageParser.Package pkg = scanPackageLI(file,
                     flags|PackageParser.PARSE_MUST_BE_APK, scanMode);
         }
     }
@@ -2009,14 +2015,15 @@
      *  Returns null in case of errors and the error code is stored in mLastScanError
      */
     private PackageParser.Package scanPackageLI(File scanFile,
-            File destCodeFile, File destResourceFile, int parseFlags,
+            int parseFlags,
             int scanMode) {
         mLastScanError = PackageManager.INSTALL_SUCCEEDED;
         parseFlags |= mDefParseFlags;
         PackageParser pp = new PackageParser(scanFile.getPath());
         pp.setSeparateProcesses(mSeparateProcesses);
         final PackageParser.Package pkg = pp.parsePackage(scanFile,
-                destCodeFile.getAbsolutePath(), mMetrics, parseFlags);
+                scanFile.getPath(),
+                mMetrics, parseFlags);
         if (pkg == null) {
             mLastScanError = pp.getParseError();
             return null;
@@ -2062,16 +2069,12 @@
         }
         // The apk is forward locked (not public) if its code and resources
         // are kept in different files.
+        // TODO grab this value from PackageSettings
         if (ps != null && !ps.codePath.equals(ps.resourcePath)) {
-            scanMode |= SCAN_FORWARD_LOCKED;
-        }
-        File resFile = destResourceFile;
-        if (ps != null && ((scanMode & SCAN_FORWARD_LOCKED) != 0)) {
-            resFile = getFwdLockedResource(ps.name);
+            parseFlags |= PackageParser.PARSE_FORWARD_LOCK;
         }
         // Note that we invoke the following method only if we are about to unpack an application
-        return scanPackageLI(scanFile, destCodeFile, resFile,
-                pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE);
+        return scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE);
     }
 
     private static String fixProcessName(String defProcessName,
@@ -2138,7 +2141,7 @@
             try {
                 if (forceDex || dalvik.system.DexFile.isDexOptNeeded(path)) {
                     ret = mInstaller.dexopt(path, pkg.applicationInfo.uid,
-                            !pkg.mForwardLocked);
+                            !isForwardLocked(pkg));
                     pkg.mDidDexOpt = true;
                     performed = true;
                 }
@@ -2164,9 +2167,8 @@
     }
     
     private PackageParser.Package scanPackageLI(
-        File scanFile, File destCodeFile, File destResourceFile,
         PackageParser.Package pkg, int parseFlags, int scanMode) {
-
+        File scanFile = new File(pkg.mScanPath);
         mScanningPath = scanFile;
         if (pkg == null) {
             mLastScanError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
@@ -2223,6 +2225,43 @@
             return null;
         }
 
+        // Initialize package source and resource directories
+        File destResourceFile = null;
+        File destCodeFile = null;
+        if ((scanMode & SCAN_NO_PATHS) == 0) {
+            boolean fwdLocked = (parseFlags & PackageParser.PARSE_FORWARD_LOCK) != 0;
+            final String pkgFileName = pkgName + ".apk";
+            File destDir = null;
+
+            if (fwdLocked) {
+                destDir = mDrmAppPrivateInstallDir;
+                destResourceFile = new File(mAppInstallDir, pkgName + ".zip");
+            } else {
+                boolean onSd = (parseFlags & PackageParser.PARSE_ON_SDCARD) != 0;
+                if (!onSd) {
+                    destDir = mAppInstallDir;
+                } else {
+                    String cachePath = getSdDir(pkgName);
+                    if (cachePath == null) {
+                        Log.e(TAG, "Secure container path for pkg: " + pkgName + " at location: " + cachePath +
+                                " not found");
+                        mLastScanError = PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
+                        return null;
+                    }
+                    destDir = new File(cachePath);
+                }
+                destResourceFile = new File(destDir, pkgFileName);
+            }
+            destCodeFile = new File(destDir, pkgFileName);
+            pkg.mPath = destCodeFile.getAbsolutePath();
+        } else {
+            pkg.mPath = pkg.mScanPath;
+            destCodeFile = new File(pkg.mScanPath);
+            destResourceFile = new File(pkg.mScanPath);
+        }
+        pkg.applicationInfo.sourceDir = destCodeFile.getAbsolutePath();
+        pkg.applicationInfo.publicSourceDir = destResourceFile.getAbsolutePath();
+
         SharedUserSetting suid = null;
         PackageSetting pkgSetting = null;
 
@@ -2394,7 +2433,6 @@
                 pkg.applicationInfo.packageName,
                 pkg.applicationInfo.processName,
                 pkg.applicationInfo.uid);
-        pkg.applicationInfo.publicSourceDir = destResourceFile.toString();
 
         File dataPath;
         if (mPlatformPackage == pkg) {
@@ -2509,8 +2547,6 @@
                     return null;
                 }
             }
-
-            pkg.mForwardLocked = (scanMode&SCAN_FORWARD_LOCKED) != 0;
             pkg.mScanPath = path;
 
             if ((scanMode&SCAN_NO_DEX) == 0) {
@@ -2545,7 +2581,7 @@
         }
         synchronized (mPackages) {
             // Add the new setting to mSettings
-            mSettings.insertPackageSettingLP(pkgSetting, pkg, destCodeFile, destResourceFile);
+            mSettings.insertPackageSettingLP(pkgSetting, pkg);
             // Add the new setting to mPackages
             mPackages.put(pkg.applicationInfo.packageName, pkg);
             int N = pkg.providers.size();
@@ -3714,7 +3750,7 @@
                 if ((event&ADD_EVENTS) != 0) {
                     PackageParser.Package p = mAppDirs.get(fullPathStr);
                     if (p == null) {
-                        p = scanPackageLI(fullPath, fullPath, fullPath,
+                        p = scanPackageLI(fullPath,
                                 (mIsRom ? PackageParser.PARSE_IS_SYSTEM : 0) |
                                 PackageParser.PARSE_CHATTY |
                                 PackageParser.PARSE_MUST_BE_APK,
@@ -3823,13 +3859,14 @@
     /*
      * Install a non-existing package.
      */
-    private void installNewPackageLI(String pkgName,
-            File tmpPackageFile,
-            String destFilePath, File destPackageFile, File destResourceFile,
-            PackageParser.Package pkg, boolean forwardLocked, boolean newInstall,
+    private void installNewPackageLI(PackageParser.Package pkg,
+            int parseFlags,
+            int scanMode,
             String installerPackageName, PackageInstalledInfo res) {
         // Remember this for later, in case we need to rollback this install
         boolean dataDirExists;
+        String pkgName = pkg.packageName;
+        boolean onSd = (parseFlags & PackageParser.PARSE_ON_SDCARD) != 0;
 
         if (useEncryptedFilesystemForPackage(pkg)) {
             dataDirExists = (new File(mSecureAppDataDir, pkgName)).exists();
@@ -3838,7 +3875,7 @@
         }
         res.name = pkgName;
         synchronized(mPackages) {
-            if (mPackages.containsKey(pkgName) || mAppDirs.containsKey(destFilePath)) {
+            if (mPackages.containsKey(pkgName) || mAppDirs.containsKey(pkg.mPath)) {
                 // Don't allow installation over an existing package with the same name.
                 Log.w(TAG, "Attempt to re-install " + pkgName
                         + " without first uninstalling.");
@@ -3846,32 +3883,37 @@
                 return;
             }
         }
-        if (destPackageFile.exists()) {
-            // It's safe to do this because we know (from the above check) that the file
-            // isn't currently used for an installed package.
-            destPackageFile.delete();
-        }
         mLastScanError = PackageManager.INSTALL_SUCCEEDED;
-        PackageParser.Package newPackage = scanPackageLI(tmpPackageFile, destPackageFile,
-                destResourceFile, pkg, 0,
-                SCAN_MONITOR | SCAN_FORCE_DEX
-                | SCAN_UPDATE_SIGNATURE
-                | (forwardLocked ? SCAN_FORWARD_LOCKED : 0)
-                | (newInstall ? SCAN_NEW_INSTALL : 0));
+        if (onSd) {
+            // Create secure container mount point for package
+            String cPath = createSdDir(new File(pkg.mScanPath), pkgName);
+            if (cPath == null) {
+                mLastScanError = res.returnCode = PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
+                return;
+            }
+        }
+        PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode);
         if (newPackage == null) {
-            Log.w(TAG, "Package couldn't be installed in " + destPackageFile);
+            Log.w(TAG, "Package couldn't be installed in " + pkg.mPath);
             if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
                 res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK;
             }
         } else {
-            updateSettingsLI(pkgName, tmpPackageFile,
-                    destFilePath, destPackageFile,
-                    destResourceFile, pkg,
-                    newPackage,
-                    true,
-                    forwardLocked,
+            File destPackageFile = new File(pkg.mPath);
+            if (destPackageFile.exists()) {
+                // It's safe to do this because we know (from the above check) that the file
+                // isn't currently used for an installed package.
+                destPackageFile.delete();
+            }
+            updateSettingsLI(newPackage,
                     installerPackageName,
                     res);
+            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
+                // Check if container can be finalized
+                if(onSd && !finalizeSdDir(pkgName)) {
+                    res.returnCode = PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
+                }
+            }
             // delete the partially installed application. the data directory will have to be
             // restored if it was already existing
             if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
@@ -3885,15 +3927,19 @@
                                 res.removedInfo);
             }
         }
+        if (onSd && res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
+            // Destroy cache
+            destroySdDir(pkgName);
+        }
     }
 
-    private void replacePackageLI(String pkgName,
-            File tmpPackageFile,
-            String destFilePath, File destPackageFile, File destResourceFile,
-            PackageParser.Package pkg, boolean forwardLocked, boolean newInstall,
+    private void replacePackageLI(PackageParser.Package pkg,
+            int parseFlags,
+            int scanMode,
             String installerPackageName, PackageInstalledInfo res) {
 
         PackageParser.Package oldPackage;
+        String pkgName = pkg.packageName;
         // First find the old package info and check signatures
         synchronized(mPackages) {
             oldPackage = mPackages.get(pkgName);
@@ -3905,21 +3951,15 @@
         }
         boolean sysPkg = ((oldPackage.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
         if(sysPkg) {
-            replaceSystemPackageLI(oldPackage,
-                    tmpPackageFile, destFilePath,
-                    destPackageFile, destResourceFile, pkg, forwardLocked,
-                    newInstall, installerPackageName, res);
+            replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanMode, installerPackageName, res);
         } else {
-            replaceNonSystemPackageLI(oldPackage, tmpPackageFile, destFilePath,
-                    destPackageFile, destResourceFile, pkg, forwardLocked,
-                    newInstall, installerPackageName, res);
+            replaceNonSystemPackageLI(oldPackage, pkg, parseFlags, scanMode, installerPackageName, res);
         }
     }
 
     private void replaceNonSystemPackageLI(PackageParser.Package deletedPackage,
-            File tmpPackageFile,
-            String destFilePath, File destPackageFile, File destResourceFile,
-            PackageParser.Package pkg, boolean forwardLocked, boolean newInstall,
+            PackageParser.Package pkg,
+            int parseFlags, int scanMode,
             String installerPackageName, PackageInstalledInfo res) {
         PackageParser.Package newPackage = null;
         String pkgName = deletedPackage.packageName;
@@ -3931,7 +3971,7 @@
             oldInstallerPackageName = mSettings.getInstallerPackageName(pkgName);
         }
 
-        int parseFlags = PackageManager.INSTALL_REPLACE_EXISTING;
+        parseFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
         // First delete the existing package while retaining the data directory
         if (!deletePackageLI(pkgName, false, PackageManager.DONT_DELETE_DATA,
                 res.removedInfo)) {
@@ -3941,24 +3981,13 @@
         } else {
             // Successfully deleted the old package. Now proceed with re-installation
             mLastScanError = PackageManager.INSTALL_SUCCEEDED;
-            newPackage = scanPackageLI(tmpPackageFile, destPackageFile,
-                    destResourceFile, pkg, parseFlags,
-                    SCAN_MONITOR | SCAN_FORCE_DEX
-                    | SCAN_UPDATE_SIGNATURE
-                    | (forwardLocked ? SCAN_FORWARD_LOCKED : 0)
-                    | (newInstall ? SCAN_NEW_INSTALL : 0));
+            newPackage = scanPackageLI(pkg, parseFlags, scanMode);
             if (newPackage == null) {
-                    Log.w(TAG, "Package couldn't be installed in " + destPackageFile);
+                Log.w(TAG, "Package couldn't be installed in " + pkg.mPath);
                 if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
                     res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK;
-                }
-            } else {
-                updateSettingsLI(pkgName, tmpPackageFile,
-                        destFilePath, destPackageFile,
-                        destResourceFile, pkg,
-                        newPackage,
-                        true,
-                        forwardLocked,
+                }      
+                updateSettingsLI(newPackage,
                         installerPackageName,
                         res);
                 updatedSettings = true;
@@ -4028,13 +4057,12 @@
     }
 
     private void replaceSystemPackageLI(PackageParser.Package deletedPackage,
-            File tmpPackageFile,
-            String destFilePath, File destPackageFile, File destResourceFile,
-            PackageParser.Package pkg, boolean forwardLocked, boolean newInstall,
+            PackageParser.Package pkg,
+            int parseFlags, int scanMode,
             String installerPackageName, PackageInstalledInfo res) {
         PackageParser.Package newPackage = null;
         boolean updatedSettings = false;
-        int parseFlags = PackageManager.INSTALL_REPLACE_EXISTING |
+        parseFlags |= PackageManager.INSTALL_REPLACE_EXISTING |
                 PackageParser.PARSE_IS_SYSTEM;
         String packageName = deletedPackage.packageName;
         res.returnCode = PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE;
@@ -4064,26 +4092,14 @@
         // Successfully disabled the old package. Now proceed with re-installation
         mLastScanError = PackageManager.INSTALL_SUCCEEDED;
         pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
-        newPackage = scanPackageLI(tmpPackageFile, destPackageFile,
-                destResourceFile, pkg, parseFlags,
-                SCAN_MONITOR | SCAN_FORCE_DEX
-                | SCAN_UPDATE_SIGNATURE
-                | (forwardLocked ? SCAN_FORWARD_LOCKED : 0)
-                | (newInstall ? SCAN_NEW_INSTALL : 0));
+        newPackage = scanPackageLI(pkg, parseFlags, scanMode);
         if (newPackage == null) {
-            Log.w(TAG, "Package couldn't be installed in " + destPackageFile);
+            Log.w(TAG, "Package couldn't be installed in " + pkg.mPath);
             if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
                 res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK;
             }
         } else {
-            updateSettingsLI(packageName, tmpPackageFile,
-                    destFilePath, destPackageFile,
-                    destResourceFile, pkg,
-                    newPackage,
-                    true,
-                    forwardLocked,
-                    installerPackageName,
-                    res);
+            updateSettingsLI(newPackage, installerPackageName, res);
             updatedSettings = true;
         }
 
@@ -4102,9 +4118,7 @@
                 removePackageLI(newPackage, true);
             }
             // Add back the old system package
-            scanPackageLI(oldPkgSetting.codePath, oldPkgSetting.codePath,
-                    oldPkgSetting.resourcePath,
-                    oldPkg, parseFlags,
+            scanPackageLI(oldPkg, parseFlags,
                     SCAN_MONITOR
                     | SCAN_UPDATE_SIGNATURE);
             // Restore the old system information in Settings
@@ -4119,14 +4133,9 @@
         }
     }
 
-    private void updateSettingsLI(String pkgName, File tmpPackageFile,
-            String destFilePath, File destPackageFile,
-            File destResourceFile,
-            PackageParser.Package pkg,
-            PackageParser.Package newPackage,
-            boolean replacingExistingPackage,
-            boolean forwardLocked,
+    private void updateSettingsLI(PackageParser.Package newPackage,
             String installerPackageName, PackageInstalledInfo res) {
+        String pkgName = newPackage.packageName;
         synchronized (mPackages) {
             //write settings. the installStatus will be incomplete at this stage.
             //note that the new package setting would have already been
@@ -4136,11 +4145,10 @@
         }
 
         int retCode = 0;
-        if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
-            retCode = mInstaller.movedex(tmpPackageFile.toString(),
-                    destPackageFile.toString());
+        if ((newPackage.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
+            retCode = mInstaller.movedex(newPackage.mScanPath, newPackage.mPath);
             if (retCode != 0) {
-                Log.e(TAG, "Couldn't rename dex file: " + destPackageFile);
+                Log.e(TAG, "Couldn't rename dex file: " + newPackage.mPath);
                 res.returnCode =  PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
                 return;
             }
@@ -4148,22 +4156,25 @@
         // XXX There are probably some big issues here: upon doing
         // the rename, we have reached the point of no return (the
         // original .apk is gone!), so we can't fail.  Yet... we can.
-        if (!tmpPackageFile.renameTo(destPackageFile)) {
-            Log.e(TAG, "Couldn't move package file to: " + destPackageFile);
-            res.returnCode =  PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+        File scanFile = new File(newPackage.mScanPath);
+        if (!scanFile.renameTo(new File(newPackage.mPath))) {
+            Log.e(TAG, "Couldn't move package file: " + newPackage.mScanPath + " to: " + newPackage.mPath);
+            // TODO rename should work. Workaround
+            if (!FileUtils.copyFile(scanFile, new File(newPackage.mPath))) {
+                Log.e(TAG, "Couldn't move package file to: " + newPackage.mPath);
+                res.returnCode =  PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+            }
         } else {
-            res.returnCode = setPermissionsLI(pkgName, newPackage, destFilePath,
-                    destResourceFile,
-                    forwardLocked);
+            res.returnCode = setPermissionsLI(newPackage);
             if(res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
                 return;
             } else {
-                Log.d(TAG, "New package installed in " + destPackageFile);
+                Log.d(TAG, "New package installed in " + newPackage.mPath);
             }
         }
         if(res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
             if (mInstaller != null) {
-                mInstaller.rmdex(tmpPackageFile.getPath());
+                mInstaller.rmdex(newPackage.mScanPath);
             }
         }
 
@@ -4180,11 +4191,6 @@
         }
     }
 
-    private File getFwdLockedResource(String pkgName) {
-        final String publicZipFileName = pkgName + ".zip";
-        return new File(mAppInstallDir, publicZipFileName);
-    }
-
     private File copyTempInstallFile(Uri pPackageURI,
             PackageInstalledInfo res) {
         File tmpPackageFile = createTempPackageFile();
@@ -4246,46 +4252,29 @@
     private void installPackageLI(Uri pPackageURI,
             int pFlags, boolean newInstall, String installerPackageName,
             File tmpPackageFile, PackageInstalledInfo res) {
-        String pkgName = null;
-        boolean forwardLocked = false;
+        boolean forwardLocked = ((pFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
+        boolean onSd = ((pFlags & PackageManager.INSTALL_ON_SDCARD) != 0);
         boolean replacingExistingPackage = false;
+        int scanMode = SCAN_MONITOR | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE
+                | (newInstall ? SCAN_NEW_INSTALL : 0);
         // Result object to be returned
         res.returnCode = PackageManager.INSTALL_SUCCEEDED;
 
         main_flow: try {
-            pkgName = PackageParser.parsePackageName(
-                    tmpPackageFile.getAbsolutePath(), 0);
-            if (pkgName == null) {
-                Log.e(TAG, "Couldn't find a package name in : " + tmpPackageFile);
-                res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK;
-                break main_flow;
-            }
-            res.name = pkgName;
-            //initialize some variables before installing pkg
-            final String pkgFileName = pkgName + ".apk";
-            final File destDir = ((pFlags&PackageManager.INSTALL_FORWARD_LOCK) != 0)
-                                 ?  mDrmAppPrivateInstallDir
-                                 : mAppInstallDir;
-            final File destPackageFile = new File(destDir, pkgFileName);
-            final String destFilePath = destPackageFile.getAbsolutePath();
-            File destResourceFile;
-            if ((pFlags&PackageManager.INSTALL_FORWARD_LOCK) != 0) {
-                destResourceFile = getFwdLockedResource(pkgName);
-                forwardLocked = true;
-            } else {
-                destResourceFile = destPackageFile;
-            }
             // Retrieve PackageSettings and parse package
-            int parseFlags = PackageParser.PARSE_CHATTY;
+            int parseFlags = PackageParser.PARSE_CHATTY |
+                    (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0) |
+                    (onSd ? PackageParser.PARSE_ON_SDCARD : 0);
             parseFlags |= mDefParseFlags;
             PackageParser pp = new PackageParser(tmpPackageFile.getPath());
             pp.setSeparateProcesses(mSeparateProcesses);
             final PackageParser.Package pkg = pp.parsePackage(tmpPackageFile,
-                    destPackageFile.getAbsolutePath(), mMetrics, parseFlags);
+                    null, mMetrics, parseFlags);
             if (pkg == null) {
                 res.returnCode = pp.getParseError();
                 break main_flow;
             }
+            String pkgName = res.name = pkg.packageName;
             if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) {
                 if ((pFlags&PackageManager.INSTALL_ALLOW_TEST) == 0) {
                     res.returnCode = PackageManager.INSTALL_FAILED_TEST_ONLY;
@@ -4306,17 +4295,11 @@
             }
 
             if(replacingExistingPackage) {
-                replacePackageLI(pkgName,
-                        tmpPackageFile,
-                        destFilePath, destPackageFile, destResourceFile,
-                        pkg, forwardLocked, newInstall, installerPackageName,
-                        res);
+                replacePackageLI(pkg, parseFlags, scanMode,
+                        installerPackageName, res);
             } else {
-                installNewPackageLI(pkgName,
-                        tmpPackageFile,
-                        destFilePath, destPackageFile, destResourceFile,
-                        pkg, forwardLocked, newInstall, installerPackageName,
-                        res);
+                installNewPackageLI(pkg, parseFlags, scanMode,
+                        installerPackageName,res);
             }
         } finally {
             if (tmpPackageFile != null && tmpPackageFile.exists()) {
@@ -4325,13 +4308,12 @@
         }
     }
 
-    private int setPermissionsLI(String pkgName,
-            PackageParser.Package newPackage,
-            String destFilePath,
-            File destResourceFile,
-            boolean forwardLocked) {
+    private int setPermissionsLI(PackageParser.Package newPackage) {
+        String pkgName = newPackage.packageName;
         int retCode;
-        if (forwardLocked) {
+        if ((newPackage.applicationInfo.flags
+                & ApplicationInfo.FLAG_FORWARD_LOCK) != 0) {
+            File destResourceFile = new File(newPackage.applicationInfo.publicSourceDir);
             try {
                 extractPublicFiles(newPackage, destResourceFile);
             } catch (IOException e) {
@@ -4347,25 +4329,25 @@
             } else {
                 final int filePermissions =
                         FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP;
-                retCode = FileUtils.setPermissions(destFilePath, filePermissions, -1,
+                retCode = FileUtils.setPermissions(newPackage.mPath, filePermissions, -1,
                                                    newPackage.applicationInfo.uid);
             }
         } else {
             final int filePermissions =
                     FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP
                     |FileUtils.S_IROTH;
-            retCode = FileUtils.setPermissions(destFilePath, filePermissions, -1, -1);
+            retCode = FileUtils.setPermissions(newPackage.mPath, filePermissions, -1, -1);
         }
         if (retCode != 0) {
-            Log.e(TAG, "Couldn't set new package file permissions for " + destFilePath
+            Log.e(TAG, "Couldn't set new package file permissions for " +
+                    newPackage.mPath
                        + ". The return code was: " + retCode);
         }
         return PackageManager.INSTALL_SUCCEEDED;
     }
 
-    private boolean isForwardLocked(PackageParser.Package deletedPackage) {
-        final ApplicationInfo applicationInfo = deletedPackage.applicationInfo;
-        return applicationInfo.sourceDir.startsWith(mDrmAppPrivateInstallDir.getAbsolutePath());
+    private boolean isForwardLocked(PackageParser.Package pkg) {
+        return  ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0);
     }
 
     private void extractPublicFiles(PackageParser.Package newPackage,
@@ -4643,9 +4625,9 @@
             mSettings.enableSystemPackageLP(p.packageName);
         }
         // Install the system package
-        PackageParser.Package newPkg = scanPackageLI(ps.codePath, ps.codePath, ps.resourcePath,
+        PackageParser.Package newPkg = scanPackageLI(ps.codePath,
                 PackageParser.PARSE_MUST_BE_APK | PackageParser.PARSE_IS_SYSTEM,
-                SCAN_MONITOR);
+                SCAN_MONITOR | SCAN_NO_PATHS);
 
         if (newPkg == null) {
             Log.w(TAG, "Failed to restore system package:"+p.packageName+" with error:" + mLastScanError);
@@ -4749,14 +4731,32 @@
             Log.w(TAG, "Package " + p.packageName + " has no applicationInfo.");
             return false;
         }
+        boolean onSd = (p.applicationInfo.flags & ApplicationInfo.FLAG_ON_SDCARD) != 0;
+        // Mount sd container if needed
+        if (onSd) {
+            // TODO Better error handling from MountService api later
+            mountSdDir(p.packageName, Process.SYSTEM_UID) ;
+        }
+        boolean ret = false;
         if ( (p.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
             Log.i(TAG, "Removing system package:"+p.packageName);
             // When an updated system application is deleted we delete the existing resources as well and
             // fall back to existing code in system partition
-            return deleteSystemPackageLI(p, flags, outInfo);
+            ret = deleteSystemPackageLI(p, flags, outInfo);
+        } else {
+            Log.i(TAG, "Removing non-system package:"+p.packageName);
+            ret = deleteInstalledPackageLI (p, deleteCodeAndResources, flags, outInfo);
         }
-        Log.i(TAG, "Removing non-system package:"+p.packageName);
-        return deleteInstalledPackageLI (p, deleteCodeAndResources, flags, outInfo);
+        if (ret && onSd) {
+            // Post a delayed destroy on the container since there might
+            // be active processes holding open file handles to package
+            // resources which will get killed by the process killer when
+            // destroying the container. This might even kill the current
+            // process and crash the system. Delay the destroy a bit so
+            // that the active processes get to handle the uninstall broadcasts.
+            sendDelayedDestroySdDir(packageName);
+        }
+        return ret;
     }
 
     public void clearApplicationUserData(final String packageName,
@@ -6343,22 +6343,23 @@
             return p;
         }
 
-        private void insertPackageSettingLP(PackageSetting p, PackageParser.Package pkg,
-                File codePath, File resourcePath) {
+        private void insertPackageSettingLP(PackageSetting p, PackageParser.Package pkg) {
             p.pkg = pkg;
+            String codePath = pkg.applicationInfo.sourceDir;
+            String resourcePath = pkg.applicationInfo.publicSourceDir;
             // Update code path if needed
-            if (!codePath.toString().equalsIgnoreCase(p.codePathString)) {
+            if (!codePath.equalsIgnoreCase(p.codePathString)) {
                 Log.w(TAG, "Code path for pkg : " + p.pkg.packageName +
                         " changing from " + p.codePathString + " to " + codePath);
-                p.codePath = codePath;
-                p.codePathString = codePath.toString();
+                p.codePath = new File(codePath);
+                p.codePathString = codePath;
             }
             //Update resource path if needed
-            if (!resourcePath.toString().equalsIgnoreCase(p.resourcePathString)) {
+            if (!resourcePath.equalsIgnoreCase(p.resourcePathString)) {
                 Log.w(TAG, "Resource path for pkg : " + p.pkg.packageName +
                         " changing from " + p.resourcePathString + " to " + resourcePath);
-                p.resourcePath = resourcePath;
-                p.resourcePathString = resourcePath.toString();
+                p.resourcePath = new File(resourcePath);
+                p.resourcePathString = resourcePath;
             }
             // Update version code if needed
              if (pkg.mVersionCode != p.versionCode) {
@@ -7432,4 +7433,110 @@
                        || packageSettings.enabledComponents.contains(componentInfo.name));
         }
     }
+
+    // ------- apps on sdcard specific code -------
+    static final boolean DEBUG_SD_INSTALL = false;
+    final private String mSdEncryptKey = "none";
+
+    private MountService getMountService() {
+        return (MountService) ServiceManager.getService("mount");
+    }
+
+   private String createSdDir(File tmpPackageFile, String pkgName) {
+        // Create mount point via MountService
+        MountService mountService = getMountService();
+        long len = tmpPackageFile.length();
+        int mbLen = (int) (len/(1024*1024));
+        if ((len - (mbLen * 1024 * 1024)) > 0) {
+            mbLen++;
+        }
+        if (DEBUG_SD_INSTALL) Log.i(TAG, "mbLen="+mbLen);
+        String cachePath = null;
+        // Remove any pending destroy messages
+        mHandler.removeMessages(DESTROY_SD_CONTAINER, pkgName);
+        try {
+            cachePath = mountService.createSecureContainer(pkgName,
+                mbLen,
+                "vfat", mSdEncryptKey, Process.SYSTEM_UID);
+            if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to install " + pkgName + ", cachePath =" + cachePath);
+            return cachePath;
+        } catch(IllegalStateException e) {
+            Log.e(TAG, "Failed to create storage on sdcard with exception: " + e);
+        }
+        // TODO just fail here and let the user delete later on.
+        try {
+            mountService.destroySecureContainer(pkgName);
+            if (DEBUG_SD_INSTALL) Log.i(TAG, "Destroying cache for " + pkgName + ", cachePath =" + cachePath);
+        } catch(IllegalStateException e) {
+            Log.e(TAG, "Failed to destroy existing cache: " + e);
+            return null;
+        } 
+       try {
+            cachePath = mountService.createSecureContainer(pkgName,
+                mbLen,
+                "vfat", mSdEncryptKey, Process.SYSTEM_UID);
+            if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to install again " + pkgName + ", cachePath =" + cachePath);
+            return cachePath;
+        } catch(IllegalStateException e) {
+            Log.e(TAG, "Failed to create storage on sdcard with exception: " + e);
+            return null;
+        }
+    }
+
+   private String mountSdDir(String pkgName, int ownerUid) {
+       try {
+           return getMountService().mountSecureContainer(pkgName, mSdEncryptKey, ownerUid);
+       } catch (IllegalStateException e) {
+           Log.i(TAG, "Failed to mount container for pkg : " + pkgName + " exception : " + e);
+       }
+       return null;
+   }
+
+   private String getSdDir(String pkgName) {
+       String cachePath = null;
+       try {
+           cachePath = getMountService().getSecureContainerPath(pkgName);
+       } catch (IllegalStateException e) {
+           Log.e(TAG, "Failed to retrieve secure container path for pkg : " + pkgName + " with exception " + e);
+       }
+       return cachePath;
+   }
+
+   private boolean finalizeSdDir(String pkgName) {
+       try {
+           getMountService().finalizeSecureContainer(pkgName);
+           return true;
+       } catch (IllegalStateException e) {
+           Log.i(TAG, "Failed to destroy container for pkg : " + pkgName);
+           return false;
+       }
+   }
+
+   private boolean destroySdDir(String pkgName) {
+       try {
+           if (mHandler.hasMessages(DESTROY_SD_CONTAINER, pkgName)) {
+               // Don't have to send message again
+               mHandler.removeMessages(DESTROY_SD_CONTAINER, pkgName);
+           }
+           // We need to destroy right away
+           getMountService().destroySecureContainer(pkgName);
+           return true;
+       } catch (IllegalStateException e) {
+           Log.i(TAG, "Failed to destroy container for pkg : " + pkgName);
+           return false;
+       }
+   }
+
+   private void sendDelayedDestroySdDir(String pkgName) {
+       if (mHandler.hasMessages(DESTROY_SD_CONTAINER, pkgName)) {
+           // Don't have to send message again
+           return;
+       }
+       Message msg = mHandler.obtainMessage(DESTROY_SD_CONTAINER, pkgName);
+       mHandler.sendMessageDelayed(msg, DESTROY_SD_CONTAINER_DELAY);
+   }
+
+   public void updateExternalMediaStatus(boolean mediaStatus) {
+       // TODO
+   }
 }