Rework permissions to be retained when an app is temporarily uninstalled.

This allows us to keep the assigned permissions when apps are temporarily
removed due to the SD card being unmounted, and also if you use the
facility to uninstall an app but keep its data.

Also fixes issue #2515189: Potential permission spoofing attack in
Android (external bug 7166)

Change-Id: I2a120ec938552028c989f9e0e890c32773957738
diff --git a/services/java/com/android/server/IntentResolver.java b/services/java/com/android/server/IntentResolver.java
index 19b0a76..8ab65e9 100644
--- a/services/java/com/android/server/IntentResolver.java
+++ b/services/java/com/android/server/IntentResolver.java
@@ -91,38 +91,64 @@
         }
     }
 
-    void dumpMap(PrintWriter out, String prefix, Map<String, ArrayList<F>> map) {
+    boolean dumpMap(PrintWriter out, String titlePrefix, String title,
+            String prefix, Map<String, ArrayList<F>> map, String packageName) {
         String eprefix = prefix + "  ";
         String fprefix = prefix + "    ";
+        boolean printedSomething = false;
         for (Map.Entry<String, ArrayList<F>> e : map.entrySet()) {
-            out.print(eprefix); out.print(e.getKey()); out.println(":");
             ArrayList<F> a = e.getValue();
             final int N = a.size();
+            boolean printedHeader = false;
             for (int i=0; i<N; i++) {
-                dumpFilter(out, fprefix, a.get(i));
+                F filter = a.get(i);
+                if (packageName != null && !packageName.equals(packageForFilter(filter))) {
+                    continue;
+                }
+                if (title != null) {
+                    out.print(titlePrefix); out.println(title);
+                    title = null;
+                }
+                if (!printedHeader) {
+                    out.print(eprefix); out.print(e.getKey()); out.println(":");
+                    printedHeader = true;
+                }
+                printedSomething = true;
+                dumpFilter(out, fprefix, filter);
             }
         }
+        return printedSomething;
     }
 
-    public void dump(PrintWriter out, String prefix) {
+    public boolean dump(PrintWriter out, String title, String prefix, String packageName) {
         String innerPrefix = prefix + "  ";
-        out.print(prefix); out.println("Full MIME Types:");
-        dumpMap(out, innerPrefix, mTypeToFilter);
-        out.println(" ");
-        out.print(prefix); out.println("Base MIME Types:");
-        dumpMap(out, innerPrefix, mBaseTypeToFilter);
-        out.println(" ");
-        out.print(prefix); out.println("Wild MIME Types:");
-        dumpMap(out, innerPrefix, mWildTypeToFilter);
-        out.println(" ");
-        out.print(prefix); out.println("Schemes:");
-        dumpMap(out, innerPrefix, mSchemeToFilter);
-        out.println(" ");
-        out.print(prefix); out.println("Non-Data Actions:");
-        dumpMap(out, innerPrefix, mActionToFilter);
-        out.println(" ");
-        out.print(prefix); out.println("MIME Typed Actions:");
-        dumpMap(out, innerPrefix, mTypedActionToFilter);
+        String sepPrefix = "\n" + prefix;
+        String curPrefix = title + "\n" + prefix;
+        if (dumpMap(out, curPrefix, "Full MIME Types:", innerPrefix,
+                mTypeToFilter, packageName)) {
+            curPrefix = sepPrefix;
+        }
+        if (dumpMap(out, curPrefix, "Base MIME Types:", innerPrefix,
+                mBaseTypeToFilter, packageName)) {
+            curPrefix = sepPrefix;
+        }
+        if (dumpMap(out, curPrefix, "Wild MIME Types:", innerPrefix,
+                mWildTypeToFilter, packageName)) {
+            curPrefix = sepPrefix;
+        }
+        if (dumpMap(out, curPrefix, "Schemes:", innerPrefix,
+                mSchemeToFilter, packageName)) {
+            curPrefix = sepPrefix;
+        }
+        if (dumpMap(out, curPrefix, "Non-Data Actions:", innerPrefix,
+                mActionToFilter, packageName)) {
+            curPrefix = sepPrefix;
+        }
+        if (dumpMap(out, curPrefix, "MIME Typed Actions:", innerPrefix,
+                mTypedActionToFilter, packageName)) {
+            curPrefix = sepPrefix;
+        }
+        return curPrefix == sepPrefix;
     }
 
     private class IteratorWrapper implements Iterator<F> {
@@ -286,6 +312,10 @@
         return true;
     }
 
+    protected String packageForFilter(F filter) {
+        return null;
+    }
+    
     protected R newResult(F filter, int match) {
         return (R)filter;
     }
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 55725bd..87a744e6 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -29,7 +29,6 @@
 
 import android.app.ActivityManagerNative;
 import android.app.IActivityManager;
-import android.app.admin.DevicePolicyManager;
 import android.app.admin.IDevicePolicyManager;
 import android.app.backup.IBackupManager;
 import android.content.ComponentName;
@@ -79,14 +78,11 @@
 import android.os.FileObserver;
 import android.os.FileUtils;
 import android.os.Handler;
-import android.os.StatFs;
-import android.os.storage.StorageResultCode;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
-import android.provider.Settings;
 import android.security.SystemKeyStore;
 import android.util.*;
 import android.view.Display;
@@ -122,6 +118,17 @@
 import java.util.zip.ZipFile;
 import java.util.zip.ZipOutputStream;
 
+/**
+ * Keep track of all those .apks everywhere.
+ * 
+ * This is very central to the platform's security; please run the unit
+ * tests whenever making modifications here:
+ * 
+mmm frameworks/base/tests/AndroidTests
+adb install -r -f out/target/product/passion/data/app/AndroidTests.apk
+adb shell am instrument -w -e class com.android.unit_tests.PackageManagerTests com.android.unit_tests/android.test.InstrumentationTestRunner
+ *
+ */
 class PackageManagerService extends IPackageManager.Stub {
     private static final String TAG = "PackageManager";
     private static final boolean DEBUG_SETTINGS = false;
@@ -912,7 +919,7 @@
                     + ((SystemClock.uptimeMillis()-startTime)/1000f)
                     + " seconds");
 
-            updatePermissionsLP();
+            updatePermissionsLP(null, null, true, false);
 
             mSettings.writeLP();
 
@@ -1207,6 +1214,36 @@
         return cur;
     }
 
+    static int[] removeInt(int[] cur, int val) {
+        if (cur == null) {
+            return null;
+        }
+        final int N = cur.length;
+        for (int i=0; i<N; i++) {
+            if (cur[i] == val) {
+                int[] ret = new int[N-1];
+                if (i > 0) {
+                    System.arraycopy(cur, 0, ret, 0, i);
+                }
+                if (i < (N-1)) {
+                    System.arraycopy(cur, i, ret, i+1, N-i-1);
+                }
+                return ret;
+            }
+        }
+        return cur;
+    }
+
+    static int[] removeInts(int[] cur, int[] rem) {
+        if (rem == null) return cur;
+        if (cur == null) return cur;
+        final int N = rem.length;
+        for (int i=0; i<N; i++) {
+            cur = removeInt(cur, rem[i]);
+        }
+        return cur;
+    }
+
     PackageInfo generatePackageInfo(PackageParser.Package p, int flags) {
         if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
             // The package has been uninstalled but has retained data and resources.
@@ -1289,11 +1326,24 @@
         return new int[0];
     }
 
+    static final PermissionInfo generatePermissionInfo(
+            BasePermission bp, int flags) {
+        if (bp.perm != null) {
+            return PackageParser.generatePermissionInfo(bp.perm, flags);
+        }
+        PermissionInfo pi = new PermissionInfo();
+        pi.name = bp.name;
+        pi.packageName = bp.sourcePackage;
+        pi.nonLocalizedLabel = bp.name;
+        pi.protectionLevel = bp.protectionLevel;
+        return pi;
+    }
+    
     public PermissionInfo getPermissionInfo(String name, int flags) {
         synchronized (mPackages) {
             final BasePermission p = mSettings.mPermissions.get(name);
-            if (p != null && p.perm != null) {
-                return PackageParser.generatePermissionInfo(p.perm, flags);
+            if (p != null) {
+                return generatePermissionInfo(p, flags);
             }
             return null;
         }
@@ -1304,11 +1354,11 @@
             ArrayList<PermissionInfo> out = new ArrayList<PermissionInfo>(10);
             for (BasePermission p : mSettings.mPermissions.values()) {
                 if (group == null) {
-                    if (p.perm.info.group == null) {
-                        out.add(PackageParser.generatePermissionInfo(p.perm, flags));
+                    if (p.perm == null || p.perm.info.group == null) {
+                        out.add(generatePermissionInfo(p, flags));
                     }
                 } else {
-                    if (group.equals(p.perm.info.group)) {
+                    if (p.perm != null && group.equals(p.perm.info.group)) {
                         out.add(PackageParser.generatePermissionInfo(p.perm, flags));
                     }
                 }
@@ -1600,6 +1650,7 @@
                         "Not allowed to modify non-dynamic permission "
                         + info.name);
             }
+            bp.protectionLevel = info.protectionLevel;
             bp.perm = new PackageParser.Permission(tree.perm.owner,
                     new PermissionInfo(info));
             bp.perm.info.packageName = tree.perm.info.packageName;
@@ -3268,6 +3319,7 @@
                             BasePermission tree = findPermissionTreeLP(p.info.name);
                             if (tree == null
                                     || tree.sourcePackage.equals(p.info.packageName)) {
+                                bp.packageSetting = pkgSetting;
                                 bp.perm = p;
                                 bp.uid = pkg.applicationInfo.uid;
                                 if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
@@ -3298,6 +3350,9 @@
                         r.append("DUP:");
                         r.append(p.info.name);
                     }
+                    if (bp.perm == p) {
+                        bp.protectionLevel = p.info.protectionLevel;
+                    }
                 } else {
                     Slog.w(TAG, "Permission " + p.info.name + " from package "
                             + p.info.packageName + " ignored: no group "
@@ -3723,15 +3778,7 @@
                     bp = mSettings.mPermissionTrees.get(p.info.name);
                 }
                 if (bp != null && bp.perm == p) {
-                    if (bp.type != BasePermission.TYPE_BUILTIN) {
-                        if (tree) {
-                            mSettings.mPermissionTrees.remove(p.info.name);
-                        } else {
-                            mSettings.mPermissions.remove(p.info.name);
-                        }
-                    } else {
-                        bp.perm = null;
-                    }
+                    bp.perm = null;
                     if (chatty) {
                         if (r == null) {
                             r = new StringBuilder(256);
@@ -3770,16 +3817,38 @@
         return name != null && name.endsWith(".apk");
     }
 
-    private void updatePermissionsLP() {
+    private static boolean hasPermission(PackageParser.Package pkgInfo, String perm) {
+        for (int i=pkgInfo.permissions.size()-1; i>=0; i--) {
+            if (pkgInfo.permissions.get(i).info.name.equals(perm)) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    private void updatePermissionsLP(String changingPkg,
+            PackageParser.Package pkgInfo, boolean grantPermissions, boolean replace) {
         // Make sure there are no dangling permission trees.
         Iterator<BasePermission> it = mSettings.mPermissionTrees
                 .values().iterator();
         while (it.hasNext()) {
             BasePermission bp = it.next();
-            if (bp.perm == null) {
+            if (bp.packageSetting == null) {
+                // We may not yet have parsed the package, so just see if
+                // we still know about its settings.
+                bp.packageSetting = mSettings.mPackages.get(bp.sourcePackage);
+            }
+            if (bp.packageSetting == null) {
                 Slog.w(TAG, "Removing dangling permission tree: " + bp.name
                         + " from package " + bp.sourcePackage);
                 it.remove();
+            } else if (changingPkg != null && changingPkg.equals(bp.sourcePackage)) {
+                if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) {
+                    Slog.i(TAG, "Removing old permission tree: " + bp.name
+                            + " from package " + bp.sourcePackage);
+                    grantPermissions = true;
+                    it.remove();
+                }
             }
         }
 
@@ -3792,9 +3861,10 @@
                 if (DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name="
                         + bp.name + " pkg=" + bp.sourcePackage
                         + " info=" + bp.pendingInfo);
-                if (bp.perm == null && bp.pendingInfo != null) {
+                if (bp.packageSetting == null && bp.pendingInfo != null) {
                     BasePermission tree = findPermissionTreeLP(bp.name);
                     if (tree != null) {
+                        bp.packageSetting = tree.packageSetting;
                         bp.perm = new PackageParser.Permission(tree.perm.owner,
                                 new PermissionInfo(bp.pendingInfo));
                         bp.perm.info.packageName = tree.perm.info.packageName;
@@ -3803,17 +3873,37 @@
                     }
                 }
             }
-            if (bp.perm == null) {
+            if (bp.packageSetting == null) {
+                // We may not yet have parsed the package, so just see if
+                // we still know about its settings.
+                bp.packageSetting = mSettings.mPackages.get(bp.sourcePackage);
+            }
+            if (bp.packageSetting == null) {
                 Slog.w(TAG, "Removing dangling permission: " + bp.name
                         + " from package " + bp.sourcePackage);
                 it.remove();
+            } else if (changingPkg != null && changingPkg.equals(bp.sourcePackage)) {
+                if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) {
+                    Slog.i(TAG, "Removing old permission: " + bp.name
+                            + " from package " + bp.sourcePackage);
+                    grantPermissions = true;
+                    it.remove();
+                }
             }
         }
 
         // Now update the permissions for all packages, in particular
         // replace the granted permissions of the system packages.
-        for (PackageParser.Package pkg : mPackages.values()) {
-            grantPermissionsLP(pkg, false);
+        if (grantPermissions) {
+            for (PackageParser.Package pkg : mPackages.values()) {
+                if (pkg != pkgInfo) {
+                    grantPermissionsLP(pkg, false);
+                }
+            }
+        }
+        
+        if (pkgInfo != null) {
+            grantPermissionsLP(pkgInfo, replace);
         }
     }
 
@@ -3823,7 +3913,7 @@
             return;
         }
         final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
-        boolean addedPermission = false;
+        boolean changedPermission = false;
 
         if (replace) {
             ps.permissionsFixed = false;
@@ -3841,26 +3931,26 @@
         for (int i=0; i<N; i++) {
             String name = pkg.requestedPermissions.get(i);
             BasePermission bp = mSettings.mPermissions.get(name);
-            PackageParser.Permission p = bp != null ? bp.perm : null;
             if (false) {
                 if (gp != ps) {
                     Log.i(TAG, "Package " + pkg.packageName + " checking " + name
-                            + ": " + p);
+                            + ": " + bp);
                 }
             }
-            if (p != null) {
-                final String perm = p.info.name;
+            if (bp != null && bp.packageSetting != null) {
+                final String perm = bp.name;
                 boolean allowed;
-                if (p.info.protectionLevel == PermissionInfo.PROTECTION_NORMAL
-                        || p.info.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS) {
+                boolean allowedSig = false;
+                if (bp.protectionLevel == PermissionInfo.PROTECTION_NORMAL
+                        || bp.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS) {
                     allowed = true;
-                } else if (p.info.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE
-                        || p.info.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) {
-                    allowed = (checkSignaturesLP(p.owner.mSignatures, pkg.mSignatures)
+                } else if (bp.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE
+                        || bp.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) {
+                    allowed = (checkSignaturesLP(bp.packageSetting.signatures.mSignatures, pkg.mSignatures)
                                     == PackageManager.SIGNATURE_MATCH)
                             || (checkSignaturesLP(mPlatformPackage.mSignatures, pkg.mSignatures)
                                     == PackageManager.SIGNATURE_MATCH);
-                    if (p.info.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) {
+                    if (bp.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) {
                         if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
                             // For updated system applications, the signatureOrSystem permission
                             // is granted only if it had been defined by the original application.
@@ -3877,6 +3967,9 @@
                             }
                         }
                     }
+                    if (allowed) {
+                        allowedSig = true;
+                    }
                 } else {
                     allowed = false;
                 }
@@ -3890,7 +3983,7 @@
                             && ps.permissionsFixed) {
                         // If this is an existing, non-system package, then
                         // we can't add any new permissions to it.
-                        if (!gp.loadedPermissions.contains(perm)) {
+                        if (!allowedSig && !gp.loadedPermissions.contains(perm)) {
                             allowed = false;
                             // Except...  if this is a permission that was added
                             // to the platform (note: need to only do this when
@@ -3911,7 +4004,7 @@
                     }
                     if (allowed) {
                         if (!gp.grantedPermissions.contains(perm)) {
-                            addedPermission = true;
+                            changedPermission = true;
                             gp.grantedPermissions.add(perm);
                             gp.gids = appendInts(gp.gids, bp.gids);
                         }
@@ -3921,11 +4014,21 @@
                                 + " because it was previously installed without");
                     }
                 } else {
-                    Slog.w(TAG, "Not granting permission " + perm
-                            + " to package " + pkg.packageName
-                            + " (protectionLevel=" + p.info.protectionLevel
-                            + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
-                            + ")");
+                    if (gp.grantedPermissions.remove(perm)) {
+                        changedPermission = true;
+                        gp.gids = removeInts(gp.gids, bp.gids);
+                        Slog.i(TAG, "Un-granting permission " + perm
+                                + " from package " + pkg.packageName
+                                + " (protectionLevel=" + bp.protectionLevel
+                                + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
+                                + ")");
+                    } else {
+                        Slog.w(TAG, "Not granting permission " + perm
+                                + " to package " + pkg.packageName
+                                + " (protectionLevel=" + bp.protectionLevel
+                                + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
+                                + ")");
+                    }
                 }
             } else {
                 Slog.w(TAG, "Unknown permission " + name
@@ -3933,7 +4036,7 @@
             }
         }
 
-        if ((addedPermission || replace) && !ps.permissionsFixed &&
+        if ((changedPermission || replace) && !ps.permissionsFixed &&
                 ((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) ||
                 ((ps.pkgFlags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0)){
             // This is the first that we have heard about this package, so the
@@ -3943,7 +4046,7 @@
             gp.loadedPermissions = new HashSet<String>(gp.grantedPermissions);
         }
     }
-
+    
     private final class ActivityIntentResolver
             extends IntentResolver<PackageParser.ActivityIntentInfo, ResolveInfo> {
         public List queryIntent(Intent intent, String resolvedType, boolean defaultOnly) {
@@ -4030,6 +4133,11 @@
         }
 
         @Override
+        protected String packageForFilter(PackageParser.ActivityIntentInfo info) {
+            return info.activity.owner.packageName;
+        }
+        
+        @Override
         protected ResolveInfo newResult(PackageParser.ActivityIntentInfo info,
                 int match) {
             if (!mSettings.isEnabledLP(info.activity.info, mFlags)) {
@@ -4069,7 +4177,9 @@
             out.print(prefix); out.print(
                     Integer.toHexString(System.identityHashCode(filter.activity)));
                     out.print(' ');
-                    out.println(filter.activity.getComponentShortName());
+                    out.print(filter.activity.getComponentShortName());
+                    out.print(" filter ");
+                    out.println(Integer.toHexString(System.identityHashCode(filter)));
         }
 
 //        List<ResolveInfo> filterEnabled(List<ResolveInfo> resolveInfoList) {
@@ -4180,6 +4290,11 @@
         }
 
         @Override
+        protected String packageForFilter(PackageParser.ServiceIntentInfo info) {
+            return info.service.owner.packageName;
+        }
+        
+        @Override
         protected ResolveInfo newResult(PackageParser.ServiceIntentInfo filter,
                 int match) {
             final PackageParser.ServiceIntentInfo info = (PackageParser.ServiceIntentInfo)filter;
@@ -4220,7 +4335,9 @@
             out.print(prefix); out.print(
                     Integer.toHexString(System.identityHashCode(filter.service)));
                     out.print(' ');
-                    out.println(filter.service.getComponentShortName());
+                    out.print(filter.service.getComponentShortName());
+                    out.print(" filter ");
+                    out.println(Integer.toHexString(System.identityHashCode(filter)));
         }
 
 //        List<ResolveInfo> filterEnabled(List<ResolveInfo> resolveInfoList) {
@@ -4389,7 +4506,8 @@
                                 SCAN_MONITOR | SCAN_NO_PATHS);
                         if (p != null) {
                             synchronized (mPackages) {
-                                grantPermissionsLP(p, false);
+                                updatePermissionsLP(p.packageName, p,
+                                        p.permissions.size() > 0, false);
                             }
                             addedPackage = p.applicationInfo.packageName;
                             addedUid = p.applicationInfo.uid;
@@ -5410,7 +5528,8 @@
                 parseFlags |= ~PackageManager.INSTALL_REPLACE_EXISTING;
                 scanPackageLI(restoreFile, parseFlags, scanMode);
                 synchronized (mPackages) {
-                    grantPermissionsLP(deletedPackage, false);
+                    updatePermissionsLP(deletedPackage.packageName, deletedPackage,
+                            true, false);
                     mSettings.writeLP();
                 }
                 if (restoreRes.returnCode != PackageManager.INSTALL_SUCCEEDED) {
@@ -5536,7 +5655,8 @@
             Log.d(TAG, "New package installed in " + newPackage.mPath);
         }
         synchronized (mPackages) {
-            grantPermissionsLP(newPackage, true);
+            updatePermissionsLP(newPackage.packageName, newPackage,
+                    newPackage.permissions.size() > 0, true);
             res.name = pkgName;
             res.uid = newPackage.applicationInfo.uid;
             res.pkg = newPackage;
@@ -5917,19 +6037,23 @@
                 File dataDir = new File(pkg.applicationInfo.dataDir);
                 dataDir.delete();
             }
-            schedulePackageCleaning(packageName);
-            synchronized (mPackages) {
-                if (outInfo != null) {
-                    outInfo.removedUid = mSettings.removePackageLP(packageName);
-                }
-            }
         }
         synchronized (mPackages) {
-            if ( (deletedPs != null) && (deletedPs.sharedUser != null)) {
-                // remove permissions associated with package
-                mSettings.updateSharedUserPermsLP(deletedPs, mGlobalGids);
-            }
             if (deletedPs != null) {
+                schedulePackageCleaning(packageName);
+                
+                if ((flags&PackageManager.DONT_DELETE_DATA) == 0) {
+                    if (outInfo != null) {
+                        outInfo.removedUid = mSettings.removePackageLP(packageName);
+                    }
+                    if (deletedPs != null) {
+                        updatePermissionsLP(deletedPs.name, null, false, false);
+                        if (deletedPs.sharedUser != null) {
+                            // remove permissions associated with package
+                            mSettings.updateSharedUserPermsLP(deletedPs, mGlobalGids);
+                        }
+                    }
+                }
                 // remove from preferred activities.
                 ArrayList<PreferredActivity> removed = new ArrayList<PreferredActivity>();
                 for (PreferredActivity pa : mSettings.mPreferredActivities.filterSet()) {
@@ -6003,7 +6127,7 @@
             return false;
         }
         synchronized (mPackages) {
-            grantPermissionsLP(newPkg, true);
+            updatePermissionsLP(newPkg.packageName, newPkg, true, true);
             mSettings.writeLP();
         }
         return true;
@@ -6659,35 +6783,102 @@
             return;
         }
 
+        String packageName = null;
+        
+        int opti = 0;
+        while (opti < args.length) {
+            String opt = args[opti];
+            if (opt == null || opt.length() <= 0 || opt.charAt(0) != '-') {
+                break;
+            }
+            opti++;
+            if ("-a".equals(opt)) {
+                // Right now we only know how to print all.
+            } else if ("-h".equals(opt)) {
+                pw.println("Package manager dump options:");
+                pw.println("  [-h] [cmd] ...");
+                pw.println("  cmd may be one of:");
+                pw.println("    [package.name]: info about given package");
+                return;
+            } else {
+                pw.println("Unknown argument: " + opt + "; use -h for help");
+            }
+        }
+        
+        // Is the caller requesting to dump a particular piece of data?
+        if (opti < args.length) {
+            String cmd = args[opti];
+            opti++;
+            // Is this a package name?
+            if ("android".equals(cmd) || cmd.contains(".")) {
+                packageName = cmd;
+            }
+        }
+        
+        boolean printedTitle = false;
+        
         synchronized (mPackages) {
-            pw.println("Activity Resolver Table:");
-            mActivities.dump(pw, "  ");
-            pw.println(" ");
-            pw.println("Receiver Resolver Table:");
-            mReceivers.dump(pw, "  ");
-            pw.println(" ");
-            pw.println("Service Resolver Table:");
-            mServices.dump(pw, "  ");
-            pw.println(" ");
-            pw.println("Preferred Activities:");
-            mSettings.mPreferredActivities.dump(pw, "  ");
-            pw.println(" ");
-            pw.println("Permissions:");
+            if (mActivities.dump(pw, "Activity Resolver Table:", "  ", packageName)) {
+                printedTitle = true;
+            }
+            if (mReceivers.dump(pw, printedTitle
+                    ? "\nReceiver Resolver Table:" : "Receiver Resolver Table:",
+                    "  ", packageName)) {
+                printedTitle = true;
+            }
+            if (mServices.dump(pw, printedTitle
+                    ? "\nService Resolver Table:" : "Service Resolver Table:",
+                    "  ", packageName)) {
+                printedTitle = true;
+            }
+            if (mSettings.mPreferredActivities.dump(pw, printedTitle
+                    ? "\nPreferred Activities:" : "Preferred Activities:",
+                    "  ", packageName)) {
+                printedTitle = true;
+            }
+            boolean printedSomething = false;
             {
                 for (BasePermission p : mSettings.mPermissions.values()) {
+                    if (packageName != null && !packageName.equals(p.sourcePackage)) {
+                        continue;
+                    }
+                    if (!printedSomething) {
+                        if (printedTitle) pw.println(" ");
+                        pw.println("Permissions:");
+                        printedSomething = true;
+                        printedTitle = true;
+                    }
                     pw.print("  Permission ["); pw.print(p.name); pw.print("] (");
                             pw.print(Integer.toHexString(System.identityHashCode(p)));
                             pw.println("):");
                     pw.print("    sourcePackage="); pw.println(p.sourcePackage);
                     pw.print("    uid="); pw.print(p.uid);
                             pw.print(" gids="); pw.print(arrayToString(p.gids));
-                            pw.print(" type="); pw.println(p.type);
+                            pw.print(" type="); pw.print(p.type);
+                            pw.print(" prot="); pw.println(p.protectionLevel);
+                    if (p.packageSetting != null) {
+                        pw.print("    packageSetting="); pw.println(p.packageSetting);
+                    }
+                    if (p.perm != null) {
+                        pw.print("    perm="); pw.println(p.perm);
+                    }
                 }
             }
-            pw.println(" ");
-            pw.println("Packages:");
+            printedSomething = false;
+            SharedUserSetting packageSharedUser = null;
             {
                 for (PackageSetting ps : mSettings.mPackages.values()) {
+                    if (packageName != null && !packageName.equals(ps.realName)
+                            && !packageName.equals(ps.name)) {
+                        continue;
+                    }
+                    if (!printedSomething) {
+                        if (printedTitle) pw.println(" ");
+                        pw.println("Packages:");
+                        printedSomething = true;
+                        printedTitle = true;
+                    }
+                    packageSharedUser = ps.sharedUser;
                     pw.print("  Package [");
                             pw.print(ps.realName != null ? ps.realName : ps.name);
                             pw.print("] (");
@@ -6771,20 +6962,38 @@
                     }
                 }
             }
+            printedSomething = false;
             if (mSettings.mRenamedPackages.size() > 0) {
-                pw.println(" ");
-                pw.println("Renamed packages:");
                 for (HashMap.Entry<String, String> e
                         : mSettings.mRenamedPackages.entrySet()) {
+                    if (packageName != null && !packageName.equals(e.getKey())
+                            && !packageName.equals(e.getValue())) {
+                        continue;
+                    }
+                    if (!printedSomething) {
+                        if (printedTitle) pw.println(" ");
+                        pw.println("Renamed packages:");
+                        printedSomething = true;
+                        printedTitle = true;
+                    }
                     pw.print("  "); pw.print(e.getKey()); pw.print(" -> ");
                             pw.println(e.getValue());
                 }
             }
+            printedSomething = false;
             if (mSettings.mDisabledSysPackages.size() > 0) {
-                pw.println(" ");
-                pw.println("Hidden system packages:");
                 for (PackageSetting ps : mSettings.mDisabledSysPackages.values()) {
-                    pw.print("  Package [");
+                    if (packageName != null && !packageName.equals(ps.realName)
+                            && !packageName.equals(ps.name)) {
+                        continue;
+                    }
+                    if (!printedSomething) {
+                        if (printedTitle) pw.println(" ");
+                        pw.println("Hidden system packages:");
+                        printedSomething = true;
+                        printedTitle = true;
+                    }
+                   pw.print("  Package [");
                             pw.print(ps.realName != null ? ps.realName : ps.name);
                             pw.print("] (");
                             pw.print(Integer.toHexString(System.identityHashCode(ps)));
@@ -6798,10 +7007,18 @@
                     pw.print("    resourcePath="); pw.println(ps.resourcePathString);
                 }
             }
-            pw.println(" ");
-            pw.println("Shared Users:");
+            printedSomething = false;
             {
                 for (SharedUserSetting su : mSettings.mSharedUsers.values()) {
+                    if (packageName != null && su != packageSharedUser) {
+                        continue;
+                    }
+                    if (!printedSomething) {
+                        if (printedTitle) pw.println(" ");
+                        pw.println("Shared users:");
+                        printedSomething = true;
+                        printedTitle = true;
+                    }
                     pw.print("  SharedUser ["); pw.print(su.name); pw.print("] (");
                             pw.print(Integer.toHexString(System.identityHashCode(su)));
                             pw.println("):");
@@ -6818,29 +7035,40 @@
                 }
             }
             
-            pw.println(" ");
-            pw.println("Settings parse messages:");
-            pw.println(mSettings.mReadMessages.toString());
-            
-            pw.println(" ");
-            pw.println("Package warning messages:");
-            File fname = getSettingsProblemFile();
-            FileInputStream in;
-            try {
-                in = new FileInputStream(fname);
-                int avail = in.available();
-                byte[] data = new byte[avail];
-                in.read(data);
-                pw.println(new String(data));
-            } catch (FileNotFoundException e) {
-            } catch (IOException e) {
+            if (packageName == null) {
+                if (printedTitle) pw.println(" ");
+                printedTitle = true;
+                pw.println("Settings parse messages:");
+                pw.println(mSettings.mReadMessages.toString());
+                
+                pw.println(" ");
+                pw.println("Package warning messages:");
+                File fname = getSettingsProblemFile();
+                FileInputStream in;
+                try {
+                    in = new FileInputStream(fname);
+                    int avail = in.available();
+                    byte[] data = new byte[avail];
+                    in.read(data);
+                    pw.println(new String(data));
+                } catch (FileNotFoundException e) {
+                } catch (IOException e) {
+                }
             }
         }
 
         synchronized (mProviders) {
-            pw.println(" ");
-            pw.println("Registered ContentProviders:");
+            boolean printedSomething = false;
             for (PackageParser.Provider p : mProviders.values()) {
+                if (packageName != null && !packageName.equals(p.info.packageName)) {
+                    continue;
+                }
+                if (!printedSomething) {
+                    if (printedTitle) pw.println(" ");
+                    pw.println("Registered ContentProviders:");
+                    printedSomething = true;
+                    printedTitle = true;
+                }
                 pw.print("  ["); pw.print(p.info.authority); pw.print("]: ");
                         pw.println(p.toString());
             }
@@ -6854,7 +7082,9 @@
 
         final String name;
         String sourcePackage;
+        PackageSettingBase packageSetting;
         final int type;
+        int protectionLevel;
         PackageParser.Permission perm;
         PermissionInfo pendingInfo;
         int uid;
@@ -6864,6 +7094,14 @@
             name = _name;
             sourcePackage = _sourcePackage;
             type = _type;
+            // Default to most conservative protection level.
+            protectionLevel = PermissionInfo.PROTECTION_SIGNATURE;
+        }
+        
+        public String toString() {
+            return "BasePermission{"
+                + Integer.toHexString(System.identityHashCode(this))
+                + " " + name + "}";
         }
     }
 
@@ -7496,6 +7734,10 @@
         private final IntentResolver<PreferredActivity, PreferredActivity> mPreferredActivities =
                     new IntentResolver<PreferredActivity, PreferredActivity>() {
             @Override
+            protected String packageForFilter(PreferredActivity filter) {
+                return filter.mActivity.getPackageName();
+            }
+            @Override
             protected void dumpFilter(PrintWriter out, String prefix,
                     PreferredActivity filter) {
                 out.print(prefix); out.print(
@@ -7734,9 +7976,10 @@
                                 + " from pkg " + bp.sourcePackage
                                 + " to " + newPkg);
                         bp.sourcePackage = newPkg;
+                        bp.packageSetting = null;
                         bp.perm = null;
                         if (bp.pendingInfo != null) {
-                            bp.sourcePackage = newPkg;
+                            bp.pendingInfo.packageName = newPkg;
                         }
                         bp.uid = 0;
                         bp.gids = null;
@@ -8243,7 +8486,7 @@
                 // be set.
                 for (final String name : pkg.grantedPermissions) {
                     BasePermission bp = mPermissions.get(name);
-                    if ((bp != null) && (bp.perm != null) && (bp.perm.info != null)) {
+                    if (bp != null) {
                         // We only need to write signature or system permissions but this wont
                         // match the semantics of grantedPermissions. So write all permissions.
                         serializer.startTag(null, "item");
@@ -8337,6 +8580,11 @@
                 serializer.startTag(null, "item");
                 serializer.attribute(null, "name", bp.name);
                 serializer.attribute(null, "package", bp.sourcePackage);
+                if (bp.protectionLevel !=
+                        PermissionInfo.PROTECTION_NORMAL) {
+                    serializer.attribute(null, "protection",
+                            Integer.toString(bp.protectionLevel));
+                }
                 if (DEBUG_SETTINGS) Log.v(TAG,
                         "Writing perm: name=" + bp.name + " type=" + bp.type);
                 if (bp.type == BasePermission.TYPE_DYNAMIC) {
@@ -8352,11 +8600,6 @@
                             serializer.attribute(null, "label",
                                     pi.nonLocalizedLabel.toString());
                         }
-                        if (pi.protectionLevel !=
-                                PermissionInfo.PROTECTION_NORMAL) {
-                            serializer.attribute(null, "protection",
-                                    Integer.toString(pi.protectionLevel));
-                        }
                     }
                 }
                 serializer.endTag(null, "item");
@@ -8558,6 +8801,8 @@
                                 dynamic
                                 ? BasePermission.TYPE_DYNAMIC
                                 : BasePermission.TYPE_NORMAL);
+                        bp.protectionLevel = readInt(parser, null, "protection",
+                                PermissionInfo.PROTECTION_NORMAL);
                         if (dynamic) {
                             PermissionInfo pi = new PermissionInfo();
                             pi.packageName = sourcePackage.intern();
@@ -8565,8 +8810,7 @@
                             pi.icon = readInt(parser, null, "icon", 0);
                             pi.nonLocalizedLabel = parser.getAttributeValue(
                                     null, "label");
-                            pi.protectionLevel = readInt(parser, null, "protection",
-                                    PermissionInfo.PROTECTION_NORMAL);
+                            pi.protectionLevel = bp.protectionLevel;
                             bp.pendingInfo = pi;
                         }
                         out.put(bp.name, bp);
@@ -9322,10 +9566,6 @@
                    // Scan the package
                    if (scanPackageLI(pkg, parseFlags, SCAN_MONITOR) != null) {
                        synchronized (mPackages) {
-                           // Grant permissions
-                           grantPermissionsLP(pkg, false);
-                           // Persist settings
-                           mSettings.writeLP();
                            retCode = PackageManager.INSTALL_SUCCEEDED;
                            pkgList.add(pkg.packageName);
                            // Post process args
@@ -9344,6 +9584,10 @@
                }
            }
        }
+       synchronized (mPackages) {
+           // Persist settings
+           mSettings.writeLP();
+       }
        // Send a broadcast to let everyone know we are done processing
        if (sendUpdateBroadcast) {
            sendResourcesChangedBroadcast(true, pkgList, uidArr);
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index a437d95..2ecebed 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -9356,7 +9356,7 @@
                 dumpAll = true;
             } else if ("-h".equals(opt)) {
                 pw.println("Activity manager dump options:");
-                pw.println("  [-a] [h- [cmd] ...");
+                pw.println("  [-a] [-h] [cmd] ...");
                 pw.println("  cmd may be one of:");
                 pw.println("    activities: activity stack state");
                 pw.println("    broadcasts: broadcast state");
@@ -9756,7 +9756,7 @@
     
             pw.println(" ");
             pw.println("Receiver Resolver Table:");
-            mReceiverResolver.dump(pw, "  ");
+            mReceiverResolver.dump(pw, null, "  ", null);
             needSep = true;
         }
         
diff --git a/tests/AndroidTests/Android.mk b/tests/AndroidTests/Android.mk
index 0d29c35..c22547f 100644
--- a/tests/AndroidTests/Android.mk
+++ b/tests/AndroidTests/Android.mk
@@ -16,4 +16,6 @@
 
 include $(BUILD_PACKAGE)
 
-include $(call all-makefiles-under,$(LOCAL_PATH))
+LOCAL_STORED_PATH:= $(LOCAL_PATH)
+include $(call all-makefiles-under,$(LOCAL_STORED_PATH))
+include $(call all-makefiles-under,$(LOCAL_STORED_PATH)/apks)
diff --git a/tests/AndroidTests/apks/install_decl_perm/Android.mk b/tests/AndroidTests/apks/install_decl_perm/Android.mk
new file mode 100644
index 0000000..9dcf9a0
--- /dev/null
+++ b/tests/AndroidTests/apks/install_decl_perm/Android.mk
@@ -0,0 +1,11 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := AndroidTests_install_decl_perm
+
+include $(BUILD_PACKAGE)
+
diff --git a/tests/AndroidTests/apks/install_decl_perm/AndroidManifest.xml b/tests/AndroidTests/apks/install_decl_perm/AndroidManifest.xml
new file mode 100644
index 0000000..4387500
--- /dev/null
+++ b/tests/AndroidTests/apks/install_decl_perm/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.unit_tests.install_decl_perm">
+
+    <permission android:name="com.android.unit_tests.NORMAL"
+        android:permissionGroup="android.permission-group.COST_MONEY"
+        android:protectionLevel="normal"
+        android:label="test normal perm" />
+        
+    <permission android:name="com.android.unit_tests.DANGEROUS"
+        android:permissionGroup="android.permission-group.COST_MONEY"
+        android:protectionLevel="dangerous"
+        android:label="test dangerous perm" />
+        
+    <permission android:name="com.android.unit_tests.SIGNATURE"
+        android:permissionGroup="android.permission-group.COST_MONEY"
+        android:protectionLevel="signature"
+        android:label="test signature perm" />
+        
+    <application android:hasCode="false">
+    </application>
+</manifest>
diff --git a/tests/AndroidTests/apks/install_decl_perm/res/values/strings.xml b/tests/AndroidTests/apks/install_decl_perm/res/values/strings.xml
new file mode 100644
index 0000000..5564300
--- /dev/null
+++ b/tests/AndroidTests/apks/install_decl_perm/res/values/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Just need this dummy file to have something to build. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/tests/AndroidTests/apks/install_use_perm_good/Android.mk b/tests/AndroidTests/apks/install_use_perm_good/Android.mk
new file mode 100644
index 0000000..a25a03c
--- /dev/null
+++ b/tests/AndroidTests/apks/install_use_perm_good/Android.mk
@@ -0,0 +1,11 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := AndroidTests_install_use_perm_good
+
+include $(BUILD_PACKAGE)
+
diff --git a/tests/AndroidTests/apks/install_use_perm_good/AndroidManifest.xml b/tests/AndroidTests/apks/install_use_perm_good/AndroidManifest.xml
new file mode 100644
index 0000000..6dd3e71
--- /dev/null
+++ b/tests/AndroidTests/apks/install_use_perm_good/AndroidManifest.xml
@@ -0,0 +1,10 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.unit_tests.install_use_perm_good">
+
+    <uses-permission android:name="com.android.unit_tests.NORMAL" />
+    <uses-permission android:name="com.android.unit_tests.DANGEROUS" />
+    <uses-permission android:name="com.android.unit_tests.SIGNATURE" />
+        
+    <application android:hasCode="false">
+    </application>
+</manifest>
diff --git a/tests/AndroidTests/apks/install_use_perm_good/res/values/strings.xml b/tests/AndroidTests/apks/install_use_perm_good/res/values/strings.xml
new file mode 100644
index 0000000..5564300
--- /dev/null
+++ b/tests/AndroidTests/apks/install_use_perm_good/res/values/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Just need this dummy file to have something to build. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+</resources>
diff --git a/tests/AndroidTests/res/raw/install_decl_perm b/tests/AndroidTests/res/raw/install_decl_perm
new file mode 100644
index 0000000..6f22321
--- /dev/null
+++ b/tests/AndroidTests/res/raw/install_decl_perm
Binary files differ
diff --git a/tests/AndroidTests/res/raw/install_use_perm_good b/tests/AndroidTests/res/raw/install_use_perm_good
new file mode 100644
index 0000000..d5216f8
--- /dev/null
+++ b/tests/AndroidTests/res/raw/install_use_perm_good
Binary files differ
diff --git a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
index b988920..009c0f2d93 100755
--- a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
@@ -45,6 +45,7 @@
 import android.content.pm.PackageParser;
 import android.content.pm.PackageStats;
 import android.content.pm.IPackageManager;
+import android.content.pm.PermissionInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.content.res.Resources.NotFoundException;
@@ -372,6 +373,7 @@
         } 
         return INSTALL_LOC_ERR;
     }
+    
     private void assertInstall(PackageParser.Package pkg, int flags, int expInstallLocation) {
         try {
             String pkgName = pkg.packageName;
@@ -410,6 +412,7 @@
             failStr("failed with exception : " + e);
         }
     }
+    
     private void assertNotInstalled(String pkgName) {
         try {
             ApplicationInfo info = getPm().getApplicationInfo(pkgName, 0);
@@ -434,6 +437,95 @@
                 false, -1, PackageInfo.INSTALL_LOCATION_AUTO);
     }
 
+    static final String PERM_PACKAGE = "package";
+    static final String PERM_DEFINED = "defined";
+    static final String PERM_UNDEFINED = "undefined";
+    static final String PERM_USED = "used";
+    static final String PERM_NOTUSED = "notused";
+    
+    private void assertPermissions(String[] cmds) {
+        final PackageManager pm = getPm();
+        String pkg = null;
+        PackageInfo pkgInfo = null;
+        String mode = PERM_DEFINED;
+        int i = 0;
+        while (i < cmds.length) {
+            String cmd = cmds[i++];
+            if (cmd == PERM_PACKAGE) {
+                pkg = cmds[i++];
+                try {
+                    pkgInfo = pm.getPackageInfo(pkg,
+                            PackageManager.GET_PERMISSIONS
+                            | PackageManager.GET_UNINSTALLED_PACKAGES);
+                } catch (NameNotFoundException e) {
+                    pkgInfo = null;
+                }
+            } else if (cmd == PERM_DEFINED || cmd == PERM_UNDEFINED
+                    || cmd == PERM_USED || cmd == PERM_NOTUSED) {
+                mode = cmds[i++];
+            } else {
+                if (mode == PERM_DEFINED) {
+                    try {
+                        PermissionInfo pi = pm.getPermissionInfo(cmd, 0);
+                        assertNotNull(pi);
+                        assertEquals(pi.packageName, pkg);
+                        assertEquals(pi.name, cmd);
+                        assertNotNull(pkgInfo);
+                        boolean found = false;
+                        for (int j=0; j<pkgInfo.permissions.length && !found; j++) {
+                            if (pkgInfo.permissions[j].name.equals(cmd)) {
+                                found = true;
+                            }
+                        }
+                        if (!found) {
+                            fail("Permission not found: " + cmd);
+                        }
+                    } catch (NameNotFoundException e) {
+                        throw new RuntimeException(e);
+                    }
+                } else if (mode == PERM_UNDEFINED) {
+                    try {
+                        pm.getPermissionInfo(cmd, 0);
+                        throw new RuntimeException("Permission exists: " + cmd);
+                    } catch (NameNotFoundException e) {
+                    }
+                    if (pkgInfo != null) {
+                        boolean found = false;
+                        for (int j=0; j<pkgInfo.permissions.length && !found; j++) {
+                            if (pkgInfo.permissions[j].name.equals(cmd)) {
+                                found = true;
+                            }
+                        }
+                        if (found) {
+                            fail("Permission still exists: " + cmd);
+                        }
+                    }
+                } else if (mode == PERM_USED || mode == PERM_NOTUSED) {
+                    boolean found = false;
+                    for (int j=0; j<pkgInfo.requestedPermissions.length && !found; j++) {
+                        if (pkgInfo.requestedPermissions[j].equals(cmd)) {
+                            found = true;
+                        }
+                    }
+                    if (!found) {
+                        fail("Permission not requested: " + cmd);
+                    }
+                    if (mode == PERM_USED) {
+                        if (pm.checkPermission(cmd, pkg)
+                                != PackageManager.PERMISSION_GRANTED) {
+                            fail("Permission not granted: " + cmd);
+                        }
+                    } else {
+                        if (pm.checkPermission(cmd, pkg)
+                                != PackageManager.PERMISSION_DENIED) {
+                            fail("Permission granted: " + cmd);
+                        }
+                    }
+                }
+            }
+        }
+    }
+    
     public void clearSecureContainersForPkg(String pkgName) {
         IMountService ms = getMs();
         try {
@@ -1862,6 +1954,179 @@
        int userSetting = PackageHelper.APP_INSTALL_AUTO;
        setUserX(userSetting);
    }
+   
+    static final String BASE_PERMISSIONS_DEFINED[] = new String[] {
+        PERM_PACKAGE, "com.android.unit_tests.install_decl_perm",
+        PERM_DEFINED,
+        "com.android.unit_tests.NORMAL",
+        "com.android.unit_tests.DANGEROUS",
+        "com.android.unit_tests.SIGNATURE",
+    };
+    
+    static final String BASE_PERMISSIONS_UNDEFINED[] = new String[] {
+        PERM_PACKAGE, "com.android.unit_tests.install_decl_perm",
+        PERM_UNDEFINED,
+        "com.android.unit_tests.NORMAL",
+        "com.android.unit_tests.DANGEROUS",
+        "com.android.unit_tests.SIGNATURE",
+    };
+    
+    static final String BASE_PERMISSIONS_USED[] = new String[] {
+        PERM_PACKAGE, "com.android.unit_tests.install_use_perm_good",
+        PERM_USED,
+        "com.android.unit_tests.NORMAL",
+        "com.android.unit_tests.DANGEROUS",
+        "com.android.unit_tests.SIGNATURE",
+    };
+    
+    static final String BASE_PERMISSIONS_NOTUSED[] = new String[] {
+        PERM_PACKAGE, "com.android.unit_tests.install_use_perm_good",
+        PERM_NOTUSED,
+        "com.android.unit_tests.NORMAL",
+        "com.android.unit_tests.DANGEROUS",
+        "com.android.unit_tests.SIGNATURE",
+    };
+    
+    static final String BASE_PERMISSIONS_SIGUSED[] = new String[] {
+        PERM_PACKAGE, "com.android.unit_tests.install_use_perm_good",
+        PERM_USED,
+        "com.android.unit_tests.SIGNATURE",
+        PERM_NOTUSED,
+        "com.android.unit_tests.NORMAL",
+        "com.android.unit_tests.DANGEROUS",
+    };
+    
+    /*
+     * Ensure that permissions are properly declared.
+     */
+    public void testInstallDeclaresPermissions() {
+        InstallParams ip = null;
+        InstallParams ip2 = null;
+        try {
+            // **: Upon installing a package, are its declared permissions published?
+           
+            int iFlags = PackageManager.INSTALL_INTERNAL;
+            int iApk = R.raw.install_decl_perm;
+            ip = installFromRawResource("install.apk", iApk,
+                    iFlags, false,
+                    false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+            assertInstall(ip.pkg, iFlags, ip.pkg.installLocation);
+            assertPermissions(BASE_PERMISSIONS_DEFINED);
+           
+            // **: Upon installing package, are its permissions granted?
+           
+            int i2Flags = PackageManager.INSTALL_INTERNAL;
+            int i2Apk = R.raw.install_use_perm_good;
+            ip2 = installFromRawResource("install2.apk", i2Apk,
+                    i2Flags, false,
+                    false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+            assertInstall(ip2.pkg, i2Flags, ip2.pkg.installLocation);
+            assertPermissions(BASE_PERMISSIONS_USED);
+            
+            // **: Upon removing but not deleting, are permissions retained?
+           
+            GenericReceiver receiver = new DeleteReceiver(ip.pkg.packageName);
+           
+            try {
+                invokeDeletePackage(ip.packageURI, PackageManager.DONT_DELETE_DATA,
+                        ip.pkg.packageName, receiver);
+            } catch (Exception e) {
+                failStr(e);
+            }
+            assertPermissions(BASE_PERMISSIONS_DEFINED);
+            assertPermissions(BASE_PERMISSIONS_USED);
+           
+            // **: Upon re-installing, are permissions retained?
+           
+            ip = installFromRawResource("install.apk", iApk,
+                    iFlags | PackageManager.INSTALL_REPLACE_EXISTING, false,
+                    false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+            assertInstall(ip.pkg, iFlags, ip.pkg.installLocation);
+            assertPermissions(BASE_PERMISSIONS_DEFINED);
+            assertPermissions(BASE_PERMISSIONS_USED);
+           
+            // **: Upon deleting package, are all permissions removed?
+           
+            try {
+                invokeDeletePackage(ip.packageURI, 0,
+                        ip.pkg.packageName, receiver);
+                ip = null;
+            } catch (Exception e) {
+                failStr(e);
+            }
+            assertPermissions(BASE_PERMISSIONS_UNDEFINED);
+            assertPermissions(BASE_PERMISSIONS_NOTUSED);
+           
+            // **: Delete package using permissions; nothing to check here.
+           
+            GenericReceiver receiver2 = new DeleteReceiver(ip2.pkg.packageName);
+            try {
+                invokeDeletePackage(ip2.packageURI, 0,
+                        ip2.pkg.packageName, receiver);
+                ip2 = null;
+            } catch (Exception e) {
+                failStr(e);
+            }
+            
+            // **: Re-install package using permissions; no permissions can be granted.
+           
+            ip2 = installFromRawResource("install2.apk", i2Apk,
+                    i2Flags, false,
+                    false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+            assertInstall(ip2.pkg, i2Flags, ip2.pkg.installLocation);
+            assertPermissions(BASE_PERMISSIONS_NOTUSED);
+           
+            // **: Upon installing declaring package, are sig permissions granted
+            // to other apps (but not other perms)?
+           
+            ip = installFromRawResource("install.apk", iApk,
+                    iFlags, false,
+                    false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+            assertInstall(ip.pkg, iFlags, ip.pkg.installLocation);
+            assertPermissions(BASE_PERMISSIONS_DEFINED);
+            assertPermissions(BASE_PERMISSIONS_SIGUSED);
+           
+            // **: Re-install package using permissions; are all permissions granted?
+            
+            ip2 = installFromRawResource("install2.apk", i2Apk,
+                    i2Flags | PackageManager.INSTALL_REPLACE_EXISTING, false,
+                    false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+            assertInstall(ip2.pkg, i2Flags, ip2.pkg.installLocation);
+            assertPermissions(BASE_PERMISSIONS_NOTUSED);
+           
+            // **: Upon deleting package, are all permissions removed?
+            
+            try {
+                invokeDeletePackage(ip.packageURI, 0,
+                        ip.pkg.packageName, receiver);
+                ip = null;
+            } catch (Exception e) {
+                failStr(e);
+            }
+            assertPermissions(BASE_PERMISSIONS_UNDEFINED);
+            assertPermissions(BASE_PERMISSIONS_NOTUSED);
+            
+            // **: Delete package using permissions; nothing to check here.
+            
+            try {
+                invokeDeletePackage(ip2.packageURI, 0,
+                        ip2.pkg.packageName, receiver);
+                ip2 = null;
+            } catch (Exception e) {
+                failStr(e);
+            }
+            
+        } finally {
+            if (ip2 != null) {
+                cleanUpInstall(ip2);
+            }
+            if (ip != null) {
+                cleanUpInstall(ip);
+            }
+        }
+    }
+
+    /*---------- Recommended install location tests ----*/
     /*
      * TODO's
      * check version numbers for upgrades