Merge "Mount/Unmount secure containers Persist flags in PackageSetting. Flags are relevant to ApplicationInfo.FLAG_SYSTEM, Application.FLAG_ON_SDCARD, ApplicationInfo.FLAG_FORWARD_LOCK. New pm command to simulate mount/unmount in Pm. This will be removed when MountService/vold event generation gets fixed. Calls from MountService into PackageManager when media gets mounted/unmounted. Scan the packages and grant permissions when the sdcard gets mounted. This api might change again."
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 4953f5d..68373cb 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -92,6 +92,11 @@
             return;
         }
 
+        if ("mountsd".equals(op)) {
+            runMountSd();
+            return;
+        }
+
         if ("uninstall".equals(op)) {
             runUninstall();
             return;
@@ -637,6 +642,37 @@
         }
     }
 
+    private void runMountSd() {
+        String opt;
+        boolean mount = false;
+        while ((opt=nextOption()) != null) {
+            if (opt.equals("-m")) {
+                String mountStr = nextOptionData();
+                if (mountStr == null) {
+                    System.err.println("Error: no value specified for -m");
+                    showUsage();
+                    return;
+                }
+                if ("true".equalsIgnoreCase(mountStr)) {
+                    mount = true;
+                } else if ("false".equalsIgnoreCase(mountStr)) {
+                    mount = false;
+                } else {
+                    System.err.println("Error: no value specified for -m");
+                    showUsage();
+                    return;
+                }
+            }
+        }
+
+        try {
+            mPm.updateExternalMediaStatus(mount);
+        } catch (RemoteException e) {
+            System.err.println(e.toString());
+            System.err.println(PM_NOT_RUNNING_ERR);
+        }
+    }
+
     class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
         boolean finished;
         boolean result;
@@ -826,6 +862,7 @@
         System.err.println("       pm path PACKAGE");
         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 mountsd [-m true/false]");
         System.err.println("       pm enable PACKAGE_OR_COMPONENT");
         System.err.println("       pm disable PACKAGE_OR_COMPONENT");
         System.err.println("");
@@ -862,6 +899,9 @@
         System.err.println("  -k: keep the data and cache directories around.");
         System.err.println("after the package removal.");
         System.err.println("");
+        System.err.println("The mountsd command simulates mounting/unmounting sdcard.Options:");
+        System.err.println("  -m: true or false.");
+        System.err.println("");
         System.err.println("The enable and disable commands change the enabled state of");
         System.err.println("a given package or component (written as \"package/class\").");
     }
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index f8f8742..0d9f98f 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -28,6 +28,7 @@
 import android.net.Uri;
 import android.os.IMountService;
 import android.os.Environment;
+import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.os.UEventObserver;
 import android.os.Handler;
@@ -131,6 +132,8 @@
 
     private String  mLegacyState = Environment.MEDIA_REMOVED;
 
+    private PackageManagerService mPms;
+
     /**
      * Constructs a new MountService instance
      * 
@@ -139,6 +142,7 @@
     public MountService(Context context) {
         mContext = context;
 
+        mPms = (PackageManagerService) ServiceManager.getService("package");
         // Register a BOOT_COMPLETED handler so that we can start
         // our NativeDaemonConnector. We defer the startup so that we don't
         // start processing events before we ought-to
@@ -509,6 +513,8 @@
     void handlePossibleExplicitUnmountBroadcast(String path) {
         if (mMounted) {
             mMounted = false;
+            // Update media status on PackageManagerService to unmount packages on sdcard
+            mPms.updateExternalMediaStatus(false);
             Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, 
                     Uri.parse("file://" + path));
             mContext.sendBroadcast(intent);
@@ -739,6 +745,8 @@
 
         updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
 
+        // Update media status on PackageManagerService to unmount packages on sdcard
+        mPms.updateExternalMediaStatus(false);
         if (mShowSafeUnmountNotificationWhenUnmounted) {
             setMediaStorageNotification(
                     com.android.internal.R.string.ext_media_safe_unmount_notification_title,
@@ -800,6 +808,8 @@
     void notifyMediaMounted(String path, boolean readOnly) {
         updatePublicVolumeState(path, Environment.MEDIA_MOUNTED);
 
+        // Update media status on PackageManagerService to mount packages on sdcard
+        mPms.updateExternalMediaStatus(true);
         setMediaStorageNotification(0, 0, 0, false, false, null);
         updateUsbMassStorageNotification(false, false);
         Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED, 
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 1b6a56a..cafc804 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -4753,13 +4753,18 @@
             ret = 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);
+            if (deleteCodeAndResources) {
+                // 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);
+            } else {
+                // Just unmount the directory
+                unMountSdDir(packageName);
+            }
         }
         return ret;
     }
@@ -5866,7 +5871,9 @@
         HashSet<String> loadedPermissions = new HashSet<String>();
 
         GrantedPermissions(int pkgFlags) {
-            this.pkgFlags = pkgFlags & ApplicationInfo.FLAG_SYSTEM;
+            this.pkgFlags = (pkgFlags & ApplicationInfo.FLAG_SYSTEM) |
+                    (pkgFlags & ApplicationInfo.FLAG_FORWARD_LOCK) |
+                    (pkgFlags & ApplicationInfo.FLAG_ON_SDCARD);
         }
     }
 
@@ -6672,9 +6679,8 @@
             if (!pkg.resourcePathString.equals(pkg.codePathString)) {
                 serializer.attribute(null, "resourcePath", pkg.resourcePathString);
             }
-            serializer.attribute(null, "system",
-                    (pkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0
-                    ? "true" : "false");
+            serializer.attribute(null, "flags",
+                    Integer.toString(pkg.pkgFlags));
             serializer.attribute(null, "ts", pkg.getTimeStampStr());
             serializer.attribute(null, "version", String.valueOf(pkg.versionCode));
             if (pkg.sharedUser == null) {
@@ -7065,16 +7071,24 @@
                     } catch (NumberFormatException e) {
                     }
                 }
-                systemStr = parser.getAttributeValue(null, "system");
                 installerPackageName = parser.getAttributeValue(null, "installer");
+
+                systemStr = parser.getAttributeValue(null, "flags");
                 if (systemStr != null) {
-                    if ("true".equals(systemStr)) {
-                        pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
+                    try {
+                        pkgFlags = Integer.parseInt(systemStr);
+                    } catch (NumberFormatException e) {
                     }
                 } else {
-                    // Old settings that don't specify system...  just treat
-                    // them as system, good enough.
-                    pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
+                    // For backward compatibility
+                    systemStr = parser.getAttributeValue(null, "system");
+                    if (systemStr != null) {
+                        pkgFlags |= ("true".equalsIgnoreCase(systemStr)) ? ApplicationInfo.FLAG_SYSTEM : 0;
+                    } else {
+                        // Old settings that don't specify system...  just treat
+                        // them as system, good enough.
+                        pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
+                    }
                 }
                 timeStampStr = parser.getAttributeValue(null, "ts");
                 if (timeStampStr != null) {
@@ -7443,6 +7457,7 @@
     static final boolean DEBUG_SD_INSTALL = false;
     final private String mSdEncryptKey = "AppsOnSD";
     final private String mSdEncryptAlg = "AES";
+    private boolean mMediaMounted = false;
 
     private MountService getMountService() {
         return (MountService) ServiceManager.getService("mount");
@@ -7518,6 +7533,11 @@
        return null;
    }
 
+   private boolean unMountSdDir(String pkgName) {
+       // STOPSHIP unmount directory
+       return true;
+   }
+
    private String getSdDir(String pkgName) {
        String cachePath = null;
        try {
@@ -7553,6 +7573,15 @@
        }
    }
 
+   private String[] getSecureContainerList() {
+       try {
+           return getMountService().getSecureContainerList();
+       } catch (IllegalStateException e) {
+           Log.i(TAG, "Failed to getSecureContainerList");
+       }
+       return null;
+   }
+
    private void sendDelayedDestroySdDir(String pkgName) {
        if (mHandler.hasMessages(DESTROY_SD_CONTAINER, pkgName)) {
            // Don't have to send message again
@@ -7562,7 +7591,63 @@
        mHandler.sendMessageDelayed(msg, DESTROY_SD_CONTAINER_DELAY);
    }
 
-   public void updateExternalMediaStatus(boolean mediaStatus) {
-       // TODO
+   public void updateExternalMediaStatus(final boolean mediaStatus) {
+       if (mediaStatus == mMediaMounted) {
+           return;
+       }
+       mMediaMounted = mediaStatus;
+        // Queue up an async operation since the package installation may take a little while.
+       mHandler.post(new Runnable() {
+           public void run() {
+               mHandler.removeCallbacks(this);
+               final String list[] = getSecureContainerList();
+               if (list == null || list.length == 0) {
+                   return;
+               }
+               for (int i = 0; i < list.length; i++) {
+                   String mountPkg = list[i];
+                   // TODO compare with default package
+                   synchronized (mPackages) {
+                       PackageSetting ps = mSettings.mPackages.get(mountPkg);
+                       if (ps != null && (ps.pkgFlags & ApplicationInfo.FLAG_ON_SDCARD) != 0) {
+                           if (mediaStatus) {
+                               String pkgPath = getSdDir(mountPkg);
+                               if (pkgPath == null) {
+                                   continue;
+                               }
+                               pkgPath = ps.codePathString;
+                               int parseFlags = PackageParser.PARSE_CHATTY |
+                               PackageParser.PARSE_ON_SDCARD | mDefParseFlags;
+                               PackageParser pp = new PackageParser(pkgPath);
+                               pp.setSeparateProcesses(mSeparateProcesses);
+                               final PackageParser.Package pkg = pp.parsePackage(new File(pkgPath),
+                                       null, mMetrics, parseFlags);
+                               if (pkg == null) {
+                                   Log.w(TAG, "Failed to install package : " + mountPkg + " from sd card");
+                                   continue;
+                               }
+                               int scanMode = SCAN_MONITOR;
+                               // Scan the package
+                               if (scanPackageLI(pkg, parseFlags, scanMode) != null) {
+                                   // Grant permissions
+                                   grantPermissionsLP(pkg, false);
+                                   // Persist settings
+                                   mSettings.writeLP();
+                               } else {
+                                   Log.i(TAG, "Failed to install package: " + mountPkg + " from sdcard");
+                               }
+                           } else {
+                               // Delete package
+                               PackageRemovedInfo outInfo = new PackageRemovedInfo();
+                               boolean res = deletePackageLI(mountPkg, false, PackageManager.DONT_DELETE_DATA, outInfo);
+                               if (!res) {
+                                   Log.e(TAG, "Failed to delete pkg  from sdcard : " + mountPkg);
+                               }
+                           }
+                       }
+                   }
+               }
+           }
+       });
    }
 }