New development permissions.

These are permissions that an application can request, but won't
normally be granted.  To have the permission granted, the user
must explicitly do so through a new "adb shell pm grant" command.

I put these permissions in the "development tools" permission
group.  Looking at the stuff there, I think all of the permissions
we already had in that group should be turned to development
permissions; I don't think any of them are protecting public APIs,
and they are really not things normal applications should use.

The support this, the protectionLevel of a permission has been
modified to consist of a base protection type with additional
flags.  The signatureOrSystem permission has thus been converted
to a signature base type with a new "system" flag; you can use
"system" and/or "dangerous" flags with signature permissions as
desired.

The permissions UI has been updated to understand these new types
of permissions and know when to display them.  Along with doing
that, it also now shows you which permissions are new when updating
an existing application.

This also starts laying the ground-work for "optional" permissions
(which development permissions are a certain specialized form of).
Completing that work requires some more features in the package
manager to understand generic optional permissions (having a
facility to not apply them when installing), along with the
appropriate UI for the app and user to manage those permissions.

Change-Id: I6571785c6bb5f6b291862b7a9be584885f88f3a5
diff --git a/api/current.txt b/api/current.txt
index 0a83fe0..373420e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6181,6 +6181,8 @@
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
+    field public static final int REQUESTED_PERMISSION_GRANTED = 2; // 0x2
+    field public static final int REQUESTED_PERMISSION_REQUIRED = 1; // 0x1
     field public android.content.pm.ActivityInfo[] activities;
     field public android.content.pm.ApplicationInfo applicationInfo;
     field public android.content.pm.ConfigurationInfo[] configPreferences;
@@ -6194,6 +6196,7 @@
     field public android.content.pm.ActivityInfo[] receivers;
     field public android.content.pm.FeatureInfo[] reqFeatures;
     field public java.lang.String[] requestedPermissions;
+    field public int[] requestedPermissionsFlags;
     field public android.content.pm.ServiceInfo[] services;
     field public java.lang.String sharedUserId;
     field public int sharedUserLabel;
@@ -6416,6 +6419,10 @@
     method public java.lang.CharSequence loadDescription(android.content.pm.PackageManager);
     field public static final android.os.Parcelable.Creator CREATOR;
     field public static final int PROTECTION_DANGEROUS = 1; // 0x1
+    field public static final int PROTECTION_FLAG_DEVELOPMENT = 32; // 0x20
+    field public static final int PROTECTION_FLAG_SYSTEM = 16; // 0x10
+    field public static final int PROTECTION_MASK_BASE = 15; // 0xf
+    field public static final int PROTECTION_MASK_FLAGS = 240; // 0xf0
     field public static final int PROTECTION_NORMAL = 0; // 0x0
     field public static final int PROTECTION_SIGNATURE = 2; // 0x2
     field public static final int PROTECTION_SIGNATURE_OR_SYSTEM = 3; // 0x3
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index f457842..ac5bffe 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -126,6 +126,16 @@
             return;
         }
 
+        if ("grant".equals(op)) {
+            runGrantRevokePermission(true);
+            return;
+        }
+
+        if ("revoke".equals(op)) {
+            runGrantRevokePermission(false);
+            return;
+        }
+
         if ("set-install-location".equals(op)) {
             runSetInstallLocation();
             return;
@@ -596,8 +606,9 @@
                 if (groups && groupName == null && pi.group != null) {
                     continue;
                 }
-                if (pi.protectionLevel < startProtectionLevel
-                        || pi.protectionLevel > endProtectionLevel) {
+                final int base = pi.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
+                if (base < startProtectionLevel
+                        || base > endProtectionLevel) {
                     continue;
                 }
                 if (summary) {
@@ -627,22 +638,8 @@
                                     + loadText(pi, pi.descriptionRes,
                                             pi.nonLocalizedDescription));
                         }
-                        String protLevel = "unknown";
-                        switch(pi.protectionLevel) {
-                            case PermissionInfo.PROTECTION_DANGEROUS:
-                                protLevel = "dangerous";
-                                break;
-                            case PermissionInfo.PROTECTION_NORMAL:
-                                protLevel = "normal";
-                                break;
-                            case PermissionInfo.PROTECTION_SIGNATURE:
-                                protLevel = "signature";
-                                break;
-                            case PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM:
-                                protLevel = "signatureOrSystem";
-                                break;
-                        }
-                        System.out.println(prefix + "  protectionLevel:" + protLevel);
+                        System.out.println(prefix + "  protectionLevel:"
+                                + PermissionInfo.protectionToString(pi.protectionLevel));
                     }
                 }
             }
@@ -1063,6 +1060,36 @@
         }
     }
 
+    private void runGrantRevokePermission(boolean grant) {
+        String pkg = nextArg();
+        if (pkg == null) {
+            System.err.println("Error: no package specified");
+            showUsage();
+            return;
+        }
+        String perm = nextArg();
+        if (perm == null) {
+            System.err.println("Error: no permission specified");
+            showUsage();
+            return;
+        }
+        try {
+            if (grant) {
+                mPm.grantPermission(pkg, perm);
+            } else {
+                mPm.revokePermission(pkg, perm);
+            }
+        } catch (RemoteException e) {
+            System.err.println(e.toString());
+            System.err.println(PM_NOT_RUNNING_ERR);
+        } catch (IllegalArgumentException e) {
+            System.err.println("Bad argument: " + e.toString());
+            showUsage();
+        } catch (SecurityException e) {
+            System.err.println("Operation not allowed: " + e.toString());
+        }
+    }
+
     /**
      * Displays the package file for a package.
      * @param pckg
@@ -1158,6 +1185,8 @@
         System.err.println("       pm enable PACKAGE_OR_COMPONENT");
         System.err.println("       pm disable PACKAGE_OR_COMPONENT");
         System.err.println("       pm disable-user PACKAGE_OR_COMPONENT");
+        System.err.println("       pm grant PACKAGE PERMISSION");
+        System.err.println("       pm revoke PACKAGE PERMISSION");
         System.err.println("       pm set-install-location [0/auto] [1/internal] [2/external]");
         System.err.println("       pm get-install-location");
         System.err.println("       pm create-profile USER_NAME");
@@ -1208,6 +1237,10 @@
         System.err.println("pm enable, disable, disable-user: these commands change the enabled state");
         System.err.println("  of a given package or component (written as \"package/class\").");
         System.err.println("");
+        System.err.println("pm grant, revoke: these commands either grant or revoke permissions");
+        System.err.println("  to applications.  Only optional permissions the application has");
+        System.err.println("  declared can be granted or revoked.");
+        System.err.println("");
         System.err.println("pm get-install-location: returns the current install location.");
         System.err.println("    0 [auto]: Let system decide the best location");
         System.err.println("    1 [internal]: Install on internal device storage");
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index fee2beb..758ce09 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -332,6 +332,24 @@
     }
 
     @Override
+    public void grantPermission(String packageName, String permissionName) {
+        try {
+            mPM.grantPermission(packageName, permissionName);
+        } catch (RemoteException e) {
+            throw new RuntimeException("Package manager has died", e);
+        }
+    }
+
+    @Override
+    public void revokePermission(String packageName, String permissionName) {
+        try {
+            mPM.revokePermission(packageName, permissionName);
+        } catch (RemoteException e) {
+            throw new RuntimeException("Package manager has died", e);
+        }
+    }
+
+    @Override
     public int checkSignatures(String pkg1, String pkg2) {
         try {
             return mPM.checkSignatures(pkg1, pkg2);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index bb35c29..95b6fee 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -81,7 +81,11 @@
     boolean addPermission(in PermissionInfo info);
     
     void removePermission(String name);
-    
+
+    void grantPermission(String packageName, String permissionName);
+
+    void revokePermission(String packageName, String permissionName);
+
     boolean isProtectedBroadcast(String actionName);
     
     int checkSignatures(String pkg1, String pkg2);
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index eb05d76..415d58a 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -141,6 +141,30 @@
     public String[] requestedPermissions;
     
     /**
+     * Array of flags of all {@link android.R.styleable#AndroidManifestUsesPermission
+     * &lt;uses-permission&gt;} tags included under &lt;manifest&gt;,
+     * or null if there were none.  This is only filled in if the flag
+     * {@link PackageManager#GET_PERMISSIONS} was set.  Each value matches
+     * the corresponding entry in {@link #requestedPermissions}, and will have
+     * the flags {@link #REQUESTED_PERMISSION_REQUIRED} and
+     * {@link #REQUESTED_PERMISSION_GRANTED} set as appropriate.
+     */
+    public int[] requestedPermissionsFlags;
+
+    /**
+     * Flag for {@link #requestedPermissionsFlags}: the requested permission
+     * is required for the application to run; the user can not optionally
+     * disable it.
+     */
+    public static final int REQUESTED_PERMISSION_REQUIRED = 1<<0;
+
+    /**
+     * Flag for {@link #requestedPermissionsFlags}: the requested permission
+     * is currently granted to the application.
+     */
+    public static final int REQUESTED_PERMISSION_GRANTED = 1<<1;
+
+    /**
      * Array of all signatures read from the package file.  This is only filled
      * in if the flag {@link PackageManager#GET_SIGNATURES} was set.
      */
@@ -229,6 +253,7 @@
         dest.writeTypedArray(instrumentation, parcelableFlags);
         dest.writeTypedArray(permissions, parcelableFlags);
         dest.writeStringArray(requestedPermissions);
+        dest.writeIntArray(requestedPermissionsFlags);
         dest.writeTypedArray(signatures, parcelableFlags);
         dest.writeTypedArray(configPreferences, parcelableFlags);
         dest.writeTypedArray(reqFeatures, parcelableFlags);
@@ -266,6 +291,7 @@
         instrumentation = source.createTypedArray(InstrumentationInfo.CREATOR);
         permissions = source.createTypedArray(PermissionInfo.CREATOR);
         requestedPermissions = source.createStringArray();
+        requestedPermissionsFlags = source.createIntArray();
         signatures = source.createTypedArray(Signature.CREATOR);
         configPreferences = source.createTypedArray(ConfigurationInfo.CREATOR);
         reqFeatures = source.createTypedArray(FeatureInfo.CREATOR);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 26a9181..f2133d8 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1475,6 +1475,29 @@
     public abstract void removePermission(String name);
 
     /**
+     * Grant a permission to an application which the application does not
+     * already have.  The permission must have been requested by the application,
+     * but as an optional permission.  If the application is not allowed to
+     * hold the permission, a SecurityException is thrown.
+     * @hide
+     *
+     * @param packageName The name of the package that the permission will be
+     * granted to.
+     * @param permissionName The name of the permission.
+     */
+    public abstract void grantPermission(String packageName, String permissionName);
+
+    /**
+     * Revoke a permission that was previously granted by {@link #grantPermission}.
+     * @hide
+     *
+     * @param packageName The name of the package that the permission will be
+     * granted to.
+     * @param permissionName The name of the permission.
+     */
+    public abstract void revokePermission(String packageName, String permissionName);
+
+    /**
      * Compare the signatures of two packages to determine if the same
      * signature appears in both of them.  If they do contain the same
      * signature, then they are allowed special privileges when working
@@ -2125,7 +2148,7 @@
         if ((flags & GET_SIGNATURES) != 0) {
             packageParser.collectCertificates(pkg, 0);
         }
-        return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0);
+        return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null);
     }
 
     /**
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 8029bd5..7b4a0ad 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -51,6 +51,7 @@
 import java.security.spec.X509EncodedKeySpec;
 import java.util.ArrayList;
 import java.util.Enumeration;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.jar.Attributes;
@@ -211,7 +212,8 @@
      * @param flags indicating which optional information is included.
      */
     public static PackageInfo generatePackageInfo(PackageParser.Package p,
-            int gids[], int flags, long firstInstallTime, long lastUpdateTime) {
+            int gids[], int flags, long firstInstallTime, long lastUpdateTime,
+            HashSet<String> grantedPermissions) {
 
         final int userId = Binder.getOrigCallingUser();
 
@@ -346,8 +348,16 @@
             N = p.requestedPermissions.size();
             if (N > 0) {
                 pi.requestedPermissions = new String[N];
+                pi.requestedPermissionsFlags = new int[N];
                 for (int i=0; i<N; i++) {
-                    pi.requestedPermissions[i] = p.requestedPermissions.get(i);
+                    final String perm = p.requestedPermissions.get(i);
+                    pi.requestedPermissions[i] = perm;
+                    if (p.requestedPermissionsRequired.get(i)) {
+                        pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_REQUIRED;
+                    }
+                    if (grantedPermissions != null && grantedPermissions.contains(perm)) {
+                        pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_GRANTED;
+                    }
                 }
             }
         }
@@ -927,11 +937,14 @@
                 // that may change.
                 String name = sa.getNonResourceString(
                         com.android.internal.R.styleable.AndroidManifestUsesPermission_name);
+                boolean required = sa.getBoolean(
+                        com.android.internal.R.styleable.AndroidManifestUsesPermission_required, true);
 
                 sa.recycle();
 
                 if (name != null && !pkg.requestedPermissions.contains(name)) {
                     pkg.requestedPermissions.add(name.intern());
+                    pkg.requestedPermissionsRequired.add(required);
                 }
 
                 XmlUtils.skipCurrentTag(parser);
@@ -1419,12 +1432,24 @@
                 PermissionInfo.PROTECTION_NORMAL);
 
         sa.recycle();
-        
+
         if (perm.info.protectionLevel == -1) {
             outError[0] = "<permission> does not specify protectionLevel";
             mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
             return null;
         }
+
+        perm.info.protectionLevel = PermissionInfo.fixProtectionLevel(perm.info.protectionLevel);
+
+        if ((perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_FLAGS) != 0) {
+            if ((perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE) !=
+                    PermissionInfo.PROTECTION_SIGNATURE) {
+                outError[0] = "<permission>  protectionLevel specifies a flag but is "
+                        + "not based on signature type";
+                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                return null;
+            }
+        }
         
         if (!parseAllMetaData(res, parser, attrs, "<permission>", perm,
                 outError)) {
@@ -2951,6 +2976,7 @@
         public final ArrayList<Instrumentation> instrumentation = new ArrayList<Instrumentation>(0);
 
         public final ArrayList<String> requestedPermissions = new ArrayList<String>();
+        public final ArrayList<Boolean> requestedPermissionsRequired = new ArrayList<Boolean>();
 
         public ArrayList<String> protectedBroadcasts;
         
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 3cc884b..69b812c 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -55,6 +55,30 @@
     public static final int PROTECTION_SIGNATURE_OR_SYSTEM = 3;
 
     /**
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>system</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     */
+    public static final int PROTECTION_FLAG_SYSTEM = 0x10;
+
+    /**
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>development</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     */
+    public static final int PROTECTION_FLAG_DEVELOPMENT = 0x20;
+
+    /**
+     * Mask for {@link #protectionLevel}: the basic protection type.
+     */
+    public static final int PROTECTION_MASK_BASE = 0xf;
+
+    /**
+     * Mask for {@link #protectionLevel}: additional flag bits.
+     */
+    public static final int PROTECTION_MASK_FLAGS = 0xf0;
+
+    /**
      * The group this permission is a part of, as per
      * {@link android.R.attr#permissionGroup}.
      */
@@ -79,10 +103,47 @@
      * The level of access this permission is protecting, as per
      * {@link android.R.attr#protectionLevel}.  Values may be
      * {@link #PROTECTION_NORMAL}, {@link #PROTECTION_DANGEROUS}, or
+     * {@link #PROTECTION_SIGNATURE}.  May also include the additional
+     * flags {@link #PROTECTION_FLAG_SYSTEM} or {@link #PROTECTION_FLAG_DEVELOPMENT}
+     * (which only make sense in combination with the base
      * {@link #PROTECTION_SIGNATURE}.
      */
     public int protectionLevel;
 
+    /** @hide */
+    public static int fixProtectionLevel(int level) {
+        if (level == PROTECTION_SIGNATURE_OR_SYSTEM) {
+            level = PROTECTION_SIGNATURE | PROTECTION_FLAG_SYSTEM;
+        }
+        return level;
+    }
+
+    /** @hide */
+    public static String protectionToString(int level) {
+        String protLevel = "????";
+        switch (level&PROTECTION_MASK_BASE) {
+            case PermissionInfo.PROTECTION_DANGEROUS:
+                protLevel = "dangerous";
+                break;
+            case PermissionInfo.PROTECTION_NORMAL:
+                protLevel = "normal";
+                break;
+            case PermissionInfo.PROTECTION_SIGNATURE:
+                protLevel = "signature";
+                break;
+            case PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM:
+                protLevel = "signatureOrSystem";
+                break;
+        }
+        if ((level&PermissionInfo.PROTECTION_FLAG_SYSTEM) != 0) {
+            protLevel += "|system";
+        }
+        if ((level&PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) {
+            protLevel += "|development";
+        }
+        return protLevel;
+    }
+
     public PermissionInfo() {
     }
 
diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java
index 988760d..e6184d5 100755
--- a/core/java/android/widget/AppSecurityPermissions.java
+++ b/core/java/android/widget/AppSecurityPermissions.java
@@ -26,6 +26,12 @@
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
 import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
+import android.text.style.ForegroundColorSpan;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -61,18 +67,52 @@
         BOTH
     }
 
+    static class MyPermissionInfo extends PermissionInfo {
+        /**
+         * PackageInfo.requestedPermissionsFlags for the new package being installed.
+         */
+        int mNewReqFlags;
+
+        /**
+         * PackageInfo.requestedPermissionsFlags for the currently installed
+         * package, if it is installed.
+         */
+        int mExistingReqFlags;
+
+        /**
+         * True if this should be considered a new permission.
+         */
+        boolean mNew;
+
+        MyPermissionInfo() {
+        }
+
+        MyPermissionInfo(PermissionInfo info) {
+            super(info);
+        }
+
+        MyPermissionInfo(MyPermissionInfo info) {
+            super(info);
+            mNewReqFlags = info.mNewReqFlags;
+            mExistingReqFlags = info.mExistingReqFlags;
+            mNew = info.mNew;
+        }
+    }
+
     private final static String TAG = "AppSecurityPermissions";
     private boolean localLOGV = false;
     private Context mContext;
     private LayoutInflater mInflater;
     private PackageManager mPm;
     private LinearLayout mPermsView;
-    private Map<String, String> mDangerousMap;
-    private Map<String, String> mNormalMap;
-    private List<PermissionInfo> mPermsList;
+    private Map<String, CharSequence> mNewMap;
+    private Map<String, CharSequence> mDangerousMap;
+    private Map<String, CharSequence> mNormalMap;
+    private List<MyPermissionInfo> mPermsList;
     private String mDefaultGrpLabel;
     private String mDefaultGrpName="DefaultGrp";
     private String mPermFormat;
+    private CharSequence mNewPermPrefix;
     private Drawable mNormalIcon;
     private Drawable mDangerousIcon;
     private boolean mExpanded;
@@ -84,20 +124,23 @@
     private State mCurrentState;
     private LinearLayout mNonDangerousList;
     private LinearLayout mDangerousList;
+    private LinearLayout mNewList;
     private HashMap<String, CharSequence> mGroupLabelCache;
     private View mNoPermsView;
-    
+
     public AppSecurityPermissions(Context context, List<PermissionInfo> permList) {
         mContext = context;
         mPm = mContext.getPackageManager();
-        mPermsList = permList;
+        for (PermissionInfo pi : permList) {
+            mPermsList.add(new MyPermissionInfo(pi));
+        }
     }
     
     public AppSecurityPermissions(Context context, String packageName) {
         mContext = context;
         mPm = mContext.getPackageManager();
-        mPermsList = new ArrayList<PermissionInfo>();
-        Set<PermissionInfo> permSet = new HashSet<PermissionInfo>();
+        mPermsList = new ArrayList<MyPermissionInfo>();
+        Set<MyPermissionInfo> permSet = new HashSet<MyPermissionInfo>();
         PackageInfo pkgInfo;
         try {
             pkgInfo = mPm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
@@ -109,7 +152,7 @@
         if((pkgInfo.applicationInfo != null) && (pkgInfo.applicationInfo.uid != -1)) {
             getAllUsedPermissions(pkgInfo.applicationInfo.uid, permSet);
         }
-        for(PermissionInfo tmpInfo : permSet) {
+        for(MyPermissionInfo tmpInfo : permSet) {
             mPermsList.add(tmpInfo);
         }
     }
@@ -117,21 +160,27 @@
     public AppSecurityPermissions(Context context, PackageParser.Package pkg) {
         mContext = context;
         mPm = mContext.getPackageManager();
-        mPermsList = new ArrayList<PermissionInfo>();
-        Set<PermissionInfo> permSet = new HashSet<PermissionInfo>();
+        mPermsList = new ArrayList<MyPermissionInfo>();
+        Set<MyPermissionInfo> permSet = new HashSet<MyPermissionInfo>();
         if(pkg == null) {
             return;
         }
+
+        // Convert to a PackageInfo
+        PackageInfo info = PackageParser.generatePackageInfo(pkg, null,
+                PackageManager.GET_PERMISSIONS, 0, 0, null);
+        PackageInfo installedPkgInfo = null;
         // Get requested permissions
-        if (pkg.requestedPermissions != null) {
-            ArrayList<String> strList = pkg.requestedPermissions;
-            int size = strList.size();
-            if (size > 0) {
-                extractPerms(strList.toArray(new String[size]), permSet);
+        if (info.requestedPermissions != null) {
+            try {
+                installedPkgInfo = mPm.getPackageInfo(info.packageName,
+                        PackageManager.GET_PERMISSIONS);
+            } catch (NameNotFoundException e) {
             }
+            extractPerms(info, permSet, installedPkgInfo);
         }
         // Get permissions related to  shared user if any
-        if(pkg.mSharedUserId != null) {
+        if (pkg.mSharedUserId != null) {
             int sharedUid;
             try {
                 sharedUid = mPm.getUidForSharedUser(pkg.mSharedUserId);
@@ -141,7 +190,7 @@
             }
         }
         // Retrieve list of permissions
-        for(PermissionInfo tmpInfo : permSet) {
+        for (MyPermissionInfo tmpInfo : permSet) {
             mPermsList.add(tmpInfo);
         }
     }
@@ -159,7 +208,7 @@
                 description, dangerous, icon);
     }
     
-    private void getAllUsedPermissions(int sharedUid, Set<PermissionInfo> permSet) {
+    private void getAllUsedPermissions(int sharedUid, Set<MyPermissionInfo> permSet) {
         String sharedPkgList[] = mPm.getPackagesForUid(sharedUid);
         if(sharedPkgList == null || (sharedPkgList.length == 0)) {
             return;
@@ -170,29 +219,65 @@
     }
     
     private void getPermissionsForPackage(String packageName, 
-            Set<PermissionInfo> permSet) {
+            Set<MyPermissionInfo> permSet) {
         PackageInfo pkgInfo;
         try {
             pkgInfo = mPm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
         } catch (NameNotFoundException e) {
-            Log.w(TAG, "Could'nt retrieve permissions for package:"+packageName);
+            Log.w(TAG, "Couldn't retrieve permissions for package:"+packageName);
             return;
         }
         if ((pkgInfo != null) && (pkgInfo.requestedPermissions != null)) {
-            extractPerms(pkgInfo.requestedPermissions, permSet);
+            extractPerms(pkgInfo, permSet, pkgInfo);
         }
     }
-    
-    private void extractPerms(String strList[], Set<PermissionInfo> permSet) {
-        if((strList == null) || (strList.length == 0)) {
+
+    private void extractPerms(PackageInfo info, Set<MyPermissionInfo> permSet,
+            PackageInfo installedPkgInfo) {
+        String[] strList = info.requestedPermissions;
+        int[] flagsList = info.requestedPermissionsFlags;
+        if ((strList == null) || (strList.length == 0)) {
             return;
         }
-        for(String permName:strList) {
+        for (int i=0; i<strList.length; i++) {
+            String permName = strList[i];
+            // If we are only looking at an existing app, then we only
+            // care about permissions that have actually been granted to it.
+            if (installedPkgInfo != null && info == installedPkgInfo) {
+                if ((flagsList[i]&PackageInfo.REQUESTED_PERMISSION_GRANTED) == 0) {
+                    continue;
+                }
+            }
             try {
                 PermissionInfo tmpPermInfo = mPm.getPermissionInfo(permName, 0);
-                if(tmpPermInfo != null) {
-                    permSet.add(tmpPermInfo);
+                if (tmpPermInfo == null) {
+                    continue;
                 }
+                int existingIndex = -1;
+                if (installedPkgInfo != null
+                        && installedPkgInfo.requestedPermissions != null) {
+                    for (int j=0; j<installedPkgInfo.requestedPermissions.length; j++) {
+                        if (permName.equals(installedPkgInfo.requestedPermissions[j])) {
+                            existingIndex = j;
+                            break;
+                        }
+                    }
+                }
+                final int existingFlags = existingIndex >= 0 ?
+                        installedPkgInfo.requestedPermissionsFlags[existingIndex] : 0;
+                if (!isDisplayablePermission(tmpPermInfo, flagsList[i], existingFlags)) {
+                    // This is not a permission that is interesting for the user
+                    // to see, so skip it.
+                    continue;
+                }
+                MyPermissionInfo myPerm = new MyPermissionInfo(tmpPermInfo);
+                myPerm.mNewReqFlags = flagsList[i];
+                myPerm.mExistingReqFlags = existingFlags;
+                // This is a new permission if the app is already installed and
+                // doesn't currently hold this permission.
+                myPerm.mNew = installedPkgInfo != null
+                        && (existingFlags&PackageInfo.REQUESTED_PERMISSION_GRANTED) == 0;
+                permSet.add(myPerm);
             } catch (NameNotFoundException e) {
                 Log.i(TAG, "Ignoring unknown permission:"+permName);
             }
@@ -210,6 +295,7 @@
         mShowMore = mPermsView.findViewById(R.id.show_more);
         mShowMoreIcon = (ImageView) mShowMore.findViewById(R.id.show_more_icon);
         mShowMoreText = (TextView) mShowMore.findViewById(R.id.show_more_text);
+        mNewList = (LinearLayout) mPermsView.findViewById(R.id.new_perms_list);
         mDangerousList = (LinearLayout) mPermsView.findViewById(R.id.dangerous_perms_list);
         mNonDangerousList = (LinearLayout) mPermsView.findViewById(R.id.non_dangerous_perms_list);
         mNoPermsView = mPermsView.findViewById(R.id.no_permissions);
@@ -222,6 +308,7 @@
         // Pick up from framework resources instead.
         mDefaultGrpLabel = mContext.getString(R.string.default_permission_group);
         mPermFormat = mContext.getString(R.string.permissions_format);
+        mNewPermPrefix = mContext.getText(R.string.perms_new_perm_prefix);
         mNormalIcon = mContext.getResources().getDrawable(R.drawable.ic_text_dot);
         mDangerousIcon = mContext.getResources().getDrawable(R.drawable.ic_bullet_key_permission);
         mShowMaxIcon = mContext.getResources().getDrawable(R.drawable.expander_close_holo_dark);
@@ -233,39 +320,56 @@
     }
 
     /**
-     * Canonicalizes the group description before it is displayed to the user.
-     *
-     * TODO check for internationalization issues remove trailing '.' in str1
-     */
-    private String canonicalizeGroupDesc(String groupDesc) {
-        if ((groupDesc == null) || (groupDesc.length() == 0)) {
-            return null;
-        }
-        // Both str1 and str2 are non-null and are non-zero in size.
-        int len = groupDesc.length();
-        if(groupDesc.charAt(len-1) == '.') {
-            groupDesc = groupDesc.substring(0, len-1);
-        }
-        return groupDesc;
-    }
-
-    /**
      * Utility method that concatenates two strings defined by mPermFormat.
      * a null value is returned if both str1 and str2 are null, if one of the strings
      * is null the other non null value is returned without formatting
      * this is to placate initial error checks
      */
-    private String formatPermissions(String groupDesc, CharSequence permDesc) {
-        if(groupDesc == null) {
-            if(permDesc == null) {
-                return null;
-            }
-            return permDesc.toString();
-        }
-        groupDesc = canonicalizeGroupDesc(groupDesc);
-        if(permDesc == null) {
+    private CharSequence formatPermissions(CharSequence groupDesc, CharSequence permDesc,
+            boolean newPerms) {
+        if (permDesc == null) {
             return groupDesc;
         }
+        // Sometimes people write permission names with a trailing period;
+        // strip that if it appears.
+        int len = permDesc.length();
+        if (len > 0 && permDesc.charAt(len-1) == '.') {
+            permDesc = (permDesc.toString()).substring(0, len-1);
+        }
+        if (newPerms) {
+            if (true) {
+                // If this is a new permission, format it appropriately.
+                SpannableStringBuilder builder = new SpannableStringBuilder();
+                if (groupDesc != null) {
+                    // The previous permissions go in front, with a newline
+                    // separating them.
+                    builder.append(groupDesc);
+                    builder.append("\n");
+                }
+                Parcel parcel = Parcel.obtain();
+                TextUtils.writeToParcel(mNewPermPrefix, parcel, 0);
+                parcel.setDataPosition(0);
+                CharSequence newStr = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+                parcel.recycle();
+                builder.append(newStr);
+                builder.append(permDesc);
+                return builder;
+            } else {
+                // If this is a new permission, format it appropriately.
+                SpannableStringBuilder builder = new SpannableStringBuilder(permDesc);
+                builder.insert(0, mNewPermPrefix);
+                if (groupDesc != null) {
+                    // The previous permissions go in front, with a newline
+                    // separating them.
+                    builder.insert(0, "\n");
+                    builder.insert(0, groupDesc);
+                }
+                return builder;
+            }
+        }
+        if (groupDesc == null) {
+            return permDesc;
+        }
         // groupDesc and permDesc are non null
         return String.format(mPermFormat, groupDesc, permDesc.toString());
     }
@@ -295,9 +399,8 @@
      * Utility method that displays permissions from a map containing group name and
      * list of permission descriptions.
      */
-    private void displayPermissions(boolean dangerous) {
-        Map<String, String> permInfoMap = dangerous ? mDangerousMap : mNormalMap;
-        LinearLayout permListView = dangerous ? mDangerousList : mNonDangerousList;
+    private void displayPermissions(Map<String, CharSequence> permInfoMap,
+            LinearLayout permListView, boolean dangerous) {
         permListView.removeAllViews();
 
         Set<String> permInfoStrSet = permInfoMap.keySet();
@@ -349,17 +452,20 @@
             break;
 
         case DANGEROUS_ONLY:
-            displayPermissions(true);
+            displayPermissions(mNewMap, mNewList, true);
+            displayPermissions(mDangerousMap, mDangerousList, true);
             break;
 
         case NORMAL_ONLY:
-            displayPermissions(false);
+            displayPermissions(mNewMap, mNewList, true);
+            displayPermissions(mNormalMap, mNonDangerousList, false);
             break;
 
         case BOTH:
-            displayPermissions(true);
+            displayPermissions(mNewMap, mNewList, true);
+            displayPermissions(mDangerousMap, mDangerousList, true);
             if (mExpanded) {
-                displayPermissions(false);
+                displayPermissions(mNormalMap, mNonDangerousList, false);
                 mShowMoreIcon.setImageDrawable(mShowMaxIcon);
                 mShowMoreText.setText(R.string.perms_hide);
                 mNonDangerousList.setVisibility(View.VISIBLE);
@@ -372,22 +478,38 @@
             break;
         }
     }
-    
-    private boolean isDisplayablePermission(PermissionInfo pInfo) {
-        if(pInfo.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS ||
-                pInfo.protectionLevel == PermissionInfo.PROTECTION_NORMAL) {
+
+    private boolean isDisplayablePermission(PermissionInfo pInfo, int newReqFlags,
+            int existingReqFlags) {
+        final int base = pInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
+        // Dangerous and normal permissions are always shown to the user.
+        if (base == PermissionInfo.PROTECTION_DANGEROUS ||
+                base == PermissionInfo.PROTECTION_NORMAL) {
+            return true;
+        }
+        // Development permissions are only shown to the user if they are already
+        // granted to the app -- if we are installing an app and they are not
+        // already granted, they will not be granted as part of the install.
+        // Note we also need the app to have specified this permission is not
+        // required -- this is not technically needed, but it helps various things
+        // if we ensure apps always mark development permissions as option, so that
+        // even not knowing what a permission is we can still know whether it will
+        // be granted to the app when it is installed.
+        if ((existingReqFlags&PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0
+                && (newReqFlags&PackageInfo.REQUESTED_PERMISSION_REQUIRED) == 0
+                && (pInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) {
             return true;
         }
         return false;
     }
-    
+
     /*
      * Utility method that aggregates all permission descriptions categorized by group
      * Say group1 has perm11, perm12, perm13, the group description will be
      * perm11_Desc, perm12_Desc, perm13_Desc
      */
-    private void aggregateGroupDescs(
-            Map<String, List<PermissionInfo> > map, Map<String, String> retMap) {
+    private void aggregateGroupDescs(Map<String, List<MyPermissionInfo> > map,
+            Map<String, CharSequence> retMap, boolean newPerms) {
         if(map == null) {
             return;
         }
@@ -397,20 +519,20 @@
         Set<String> grpNames = map.keySet();
         Iterator<String> grpNamesIter = grpNames.iterator();
         while(grpNamesIter.hasNext()) {
-            String grpDesc = null;
+            CharSequence grpDesc = null;
             String grpNameKey = grpNamesIter.next();
-            List<PermissionInfo> grpPermsList = map.get(grpNameKey);
+            List<MyPermissionInfo> grpPermsList = map.get(grpNameKey);
             if(grpPermsList == null) {
                 continue;
             }
             for(PermissionInfo permInfo: grpPermsList) {
                 CharSequence permDesc = permInfo.loadLabel(mPm);
-                grpDesc = formatPermissions(grpDesc, permDesc);
+                grpDesc = formatPermissions(grpDesc, permDesc, newPerms);
             }
             // Insert grpDesc into map
             if(grpDesc != null) {
                 if(localLOGV) Log.i(TAG, "Group:"+grpNameKey+" description:"+grpDesc.toString());
-                retMap.put(grpNameKey, grpDesc.toString());
+                retMap.put(grpNameKey, grpDesc);
             }
         }
     }
@@ -428,42 +550,53 @@
         }
     }
     
-    private void setPermissions(List<PermissionInfo> permList) {
+    private void setPermissions(List<MyPermissionInfo> permList) {
         mGroupLabelCache = new HashMap<String, CharSequence>();
         //add the default label so that uncategorized permissions can go here
         mGroupLabelCache.put(mDefaultGrpName, mDefaultGrpLabel);
         
         // Map containing group names and a list of permissions under that group
+        // that are new from the current install
+        mNewMap = new HashMap<String, CharSequence>();
+        // Map containing group names and a list of permissions under that group
         // categorized as dangerous
-        mDangerousMap = new HashMap<String, String>();
+        mDangerousMap = new HashMap<String, CharSequence>();
         // Map containing group names and a list of permissions under that group
         // categorized as normal
-        mNormalMap = new HashMap<String, String>();
+        mNormalMap = new HashMap<String, CharSequence>();
         
         // Additional structures needed to ensure that permissions are unique under 
         // each group
-        Map<String, List<PermissionInfo>> dangerousMap = 
-            new HashMap<String,  List<PermissionInfo>>();
-        Map<String, List<PermissionInfo> > normalMap = 
-            new HashMap<String,  List<PermissionInfo>>();
+        Map<String, List<MyPermissionInfo>> newMap =
+            new HashMap<String,  List<MyPermissionInfo>>();
+        Map<String, List<MyPermissionInfo>> dangerousMap = 
+            new HashMap<String,  List<MyPermissionInfo>>();
+        Map<String, List<MyPermissionInfo> > normalMap = 
+            new HashMap<String,  List<MyPermissionInfo>>();
         PermissionInfoComparator permComparator = new PermissionInfoComparator(mPm);
         
         if (permList != null) {
             // First pass to group permissions
-            for (PermissionInfo pInfo : permList) {
+            for (MyPermissionInfo pInfo : permList) {
                 if(localLOGV) Log.i(TAG, "Processing permission:"+pInfo.name);
-                if(!isDisplayablePermission(pInfo)) {
+                if(!isDisplayablePermission(pInfo, pInfo.mNewReqFlags, pInfo.mExistingReqFlags)) {
                     if(localLOGV) Log.i(TAG, "Permission:"+pInfo.name+" is not displayable");
                     continue;
                 }
-                Map<String, List<PermissionInfo> > permInfoMap =
-                    (pInfo.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS) ?
-                            dangerousMap : normalMap;
+                Map<String, List<MyPermissionInfo> > permInfoMap;
+                if (pInfo.mNew) {
+                    permInfoMap = newMap;
+                } else if ((pInfo.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE)
+                            == PermissionInfo.PROTECTION_DANGEROUS) {
+                    permInfoMap = dangerousMap;
+                } else {
+                    permInfoMap = normalMap;
+                }
                 String grpName = (pInfo.group == null) ? mDefaultGrpName : pInfo.group;
                 if(localLOGV) Log.i(TAG, "Permission:"+pInfo.name+" belongs to group:"+grpName);
-                List<PermissionInfo> grpPermsList = permInfoMap.get(grpName);
+                List<MyPermissionInfo> grpPermsList = permInfoMap.get(grpName);
                 if(grpPermsList == null) {
-                    grpPermsList = new ArrayList<PermissionInfo>();
+                    grpPermsList = new ArrayList<MyPermissionInfo>();
                     permInfoMap.put(grpName, grpPermsList);
                     grpPermsList.add(pInfo);
                 } else {
@@ -477,12 +610,13 @@
             }
             // Second pass to actually form the descriptions
             // Look at dangerous permissions first
-            aggregateGroupDescs(dangerousMap, mDangerousMap);
-            aggregateGroupDescs(normalMap, mNormalMap);
+            aggregateGroupDescs(newMap, mNewMap, true);
+            aggregateGroupDescs(dangerousMap, mDangerousMap, false);
+            aggregateGroupDescs(normalMap, mNormalMap, false);
         }
 
         mCurrentState = State.NO_PERMS;
-        if(mDangerousMap.size() > 0) {
+        if (mNewMap.size() > 0 || mDangerousMap.size() > 0) {
             mCurrentState = (mNormalMap.size() > 0) ? State.BOTH : State.DANGEROUS_ONLY;
         } else if(mNormalMap.size() > 0) {
             mCurrentState = State.NORMAL_ONLY;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a2b1117..d4d29ae 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -147,7 +147,7 @@
          @hide -->
     <permission android:name="android.permission.SEND_SMS_NO_CONFIRMATION"
         android:permissionGroup="android.permission-group.COST_MONEY"
-        android:protectionLevel="signatureOrSystem"
+        android:protectionLevel="signature|system"
         android:label="@string/permlab_sendSmsNoConfirmation"
         android:description="@string/permdesc_sendSmsNoConfirmation" />
 
@@ -194,7 +194,7 @@
          @hide Pending API council approval -->
     <permission android:name="android.permission.RECEIVE_EMERGENCY_BROADCAST"
         android:permissionGroup="android.permission-group.MESSAGES"
-        android:protectionLevel="signatureOrSystem"
+        android:protectionLevel="signature|system"
         android:label="@string/permlab_receiveEmergencyBroadcast"
         android:description="@string/permdesc_receiveEmergencyBroadcast" />
 
@@ -383,7 +383,7 @@
 
     <!-- Allows an application to install a location provider into the Location Manager -->
     <permission android:name="android.permission.INSTALL_LOCATION_PROVIDER"
-        android:protectionLevel="signatureOrSystem"
+        android:protectionLevel="signature|system"
         android:label="@string/permlab_installLocationProvider"
         android:description="@string/permdesc_installLocationProvider" />
 
@@ -461,7 +461,7 @@
         @hide -->
     <permission android:name="android.permission.CONNECTIVITY_INTERNAL"
         android:permissionGroup="android.permission-group.NETWORK"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- ================================== -->
     <!-- Permissions for accessing accounts -->
@@ -559,7 +559,7 @@
          @hide -->
     <permission android:name="android.permission.MANAGE_USB"
         android:permissionGroup="android.permission-group.HARDWARE_CONTROLS"
-        android:protectionLevel="signatureOrSystem"
+        android:protectionLevel="signature|system"
         android:label="@string/permlab_manageUsb"
         android:description="@string/permdesc_manageUsb" />
 
@@ -568,7 +568,7 @@
          @hide -->
     <permission android:name="android.permission.ACCESS_MTP"
         android:permissionGroup="android.permission-group.HARDWARE_CONTROLS"
-        android:protectionLevel="signatureOrSystem"
+        android:protectionLevel="signature|system"
         android:label="@string/permlab_accessMtp"
         android:description="@string/permdesc_accessMtp" />
 
@@ -611,7 +611,7 @@
          Does not include placing calls. -->
     <permission android:name="android.permission.MODIFY_PHONE_STATE"
         android:permissionGroup="android.permission-group.PHONE_CALLS"
-        android:protectionLevel="signatureOrSystem"
+        android:protectionLevel="signature|system"
         android:label="@string/permlab_modifyPhoneState"
         android:description="@string/permdesc_modifyPhoneState" />
 
@@ -626,7 +626,7 @@
          @hide Used internally. -->
     <permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"
         android:permissionGroup="android.permission-group.PHONE_CALLS"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- ================================== -->
     <!-- Permissions for sdcard interaction -->
@@ -651,7 +651,7 @@
         android:permissionGroup="android.permission-group.STORAGE"
         android:label="@string/permlab_mediaStorageWrite"
         android:description="@string/permdesc_mediaStorageWrite"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- ============================================ -->
     <!-- Permissions for low-level system interaction -->
@@ -675,15 +675,9 @@
         android:label="@string/permlab_writeSettings"
         android:description="@string/permdesc_writeSettings" />
 
-    <!-- Allows an application to read or write the secure system settings. -->
-    <permission android:name="android.permission.WRITE_SECURE_SETTINGS"
-        android:protectionLevel="signatureOrSystem"
-        android:label="@string/permlab_writeSecureSettings"
-        android:description="@string/permdesc_writeSecureSettings" />
-
     <!-- Allows an application to modify the Google service map. -->
     <permission android:name="android.permission.WRITE_GSERVICES"
-        android:protectionLevel="signatureOrSystem"
+        android:protectionLevel="signature|system"
         android:label="@string/permlab_writeGservices"
         android:description="@string/permdesc_writeGservices" />
 
@@ -757,19 +751,11 @@
         android:label="@string/permlab_forceStopPackages"
         android:description="@string/permdesc_forceStopPackages" />
 
-    <!-- Allows an application to retrieve state dump information from system
-         services. -->
-    <permission android:name="android.permission.DUMP"
-        android:permissionGroup="android.permission-group.PERSONAL_INFO"
-        android:protectionLevel="signatureOrSystem"
-        android:label="@string/permlab_dump"
-        android:description="@string/permdesc_dump" />
-
     <!-- @hide Allows an application to retrieve the content of the active window
          An active window is the window that has fired an accessibility event. -->
     <permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT"
         android:permissionGroup="android.permission-group.PERSONAL_INFO"
-        android:protectionLevel="signatureOrSystem"
+        android:protectionLevel="signature|system"
         android:label="@string/permlab_retrieve_window_content"
         android:description="@string/permdesc_retrieve_window_content" />
 
@@ -868,7 +854,7 @@
 
     <!-- Allows applications to set the system time -->
     <permission android:name="android.permission.SET_TIME"
-        android:protectionLevel="signatureOrSystem"
+        android:protectionLevel="signature|system"
         android:label="@string/permlab_setTime"
         android:description="@string/permdesc_setTime" />
 
@@ -964,7 +950,7 @@
     <!-- Allows applications to write the apn settings -->
     <permission android:name="android.permission.WRITE_APN_SETTINGS"
                 android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
-                android:protectionLevel="signatureOrSystem"
+                android:protectionLevel="signature|system"
                 android:description="@string/permdesc_writeApnSettings"
                 android:label="@string/permlab_writeApnSettings" />
 
@@ -1035,7 +1021,7 @@
     <!-- Allows an application to use any media decoder when decoding for playback
          @hide -->
     <permission android:name="android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"
-        android:protectionLevel="signatureOrSystem"
+        android:protectionLevel="signature|system"
         android:label="@string/permlab_anyCodecForPlayback"
         android:description="@string/permdesc_anyCodecForPlayback" />
 
@@ -1052,6 +1038,21 @@
         android:label="@string/permgrouplab_developmentTools"
         android:description="@string/permgroupdesc_developmentTools" />
 
+    <!-- Allows an application to read or write the secure system settings. -->
+    <permission android:name="android.permission.WRITE_SECURE_SETTINGS"
+        android:permissionGroup="android.permission-group.DEVELOPMENT_TOOLS"
+        android:protectionLevel="signature|system|development"
+        android:label="@string/permlab_writeSecureSettings"
+        android:description="@string/permdesc_writeSecureSettings" />
+
+    <!-- Allows an application to retrieve state dump information from system
+         services. -->
+    <permission android:name="android.permission.DUMP"
+        android:permissionGroup="android.permission-group.DEVELOPMENT_TOOLS"
+        android:protectionLevel="signature|system|development"
+        android:label="@string/permlab_dump"
+        android:description="@string/permdesc_dump" />
+
     <!-- Configure an application for debugging. -->
     <permission android:name="android.permission.SET_DEBUG_APP"
         android:permissionGroup="android.permission-group.DEVELOPMENT_TOOLS"
@@ -1099,7 +1100,7 @@
     <permission android:name="android.permission.STATUS_BAR"
         android:label="@string/permlab_statusBar"
         android:description="@string/permdesc_statusBar"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows an application to be the status bar.  Currently used only by SystemUI.apk
     @hide -->
@@ -1120,7 +1121,7 @@
     <permission android:name="android.permission.UPDATE_DEVICE_STATS"
         android:label="@string/permlab_batteryStats"
         android:description="@string/permdesc_batteryStats"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows an application to open windows that are for use by parts
          of the system user interface.  Not for use by third party apps. -->
@@ -1160,7 +1161,7 @@
     <permission android:name="android.permission.SHUTDOWN"
         android:label="@string/permlab_shutdown"
         android:description="@string/permdesc_shutdown"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows an application to tell the activity manager to temporarily
          stop application switches, putting it into a special mode that
@@ -1170,7 +1171,7 @@
     <permission android:name="android.permission.STOP_APP_SWITCHES"
         android:label="@string/permlab_stopAppSwitches"
         android:description="@string/permdesc_stopAppSwitches"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows an application to retrieve the current state of keys and
          switches.  This is only for use by the system.-->
@@ -1205,7 +1206,7 @@
     <permission android:name="android.permission.BIND_WALLPAPER"
         android:label="@string/permlab_bindWallpaper"
         android:description="@string/permdesc_bindWallpaper"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Must be required by device administration receiver, to ensure that only the
          system can interact with it. -->
@@ -1232,7 +1233,7 @@
     <permission android:name="android.permission.INSTALL_PACKAGES"
         android:label="@string/permlab_installPackages"
         android:description="@string/permdesc_installPackages"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows an application to clear user data -->
     <permission android:name="android.permission.CLEAR_APP_USER_DATA"
@@ -1244,27 +1245,33 @@
     <permission android:name="android.permission.DELETE_CACHE_FILES"
         android:label="@string/permlab_deleteCacheFiles"
         android:description="@string/permdesc_deleteCacheFiles"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows an application to delete packages. -->
     <permission android:name="android.permission.DELETE_PACKAGES"
         android:label="@string/permlab_deletePackages"
         android:description="@string/permdesc_deletePackages"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows an application to move location of installed package.
          @hide -->
     <permission android:name="android.permission.MOVE_PACKAGE"
         android:label="@string/permlab_movePackage"
         android:description="@string/permdesc_movePackage"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows an application to change whether an application component (other than its own) is
          enabled or not. -->
     <permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"
         android:label="@string/permlab_changeComponentState"
         android:description="@string/permdesc_changeComponentState"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
+
+    <!-- @hide Allows an application to grant or revoke specific permissions. -->
+    <permission android:name="android.permission.GRANT_REVOKE_PERMISSIONS"
+        android:label="@string/permlab_grantRevokePermissions"
+        android:description="@string/permdesc_grantRevokePermissions"
+        android:protectionLevel="signature" />
 
     <!-- Allows an application to use SurfaceFlinger's low level features -->
     <permission android:name="android.permission.ACCESS_SURFACE_FLINGER"
@@ -1277,7 +1284,7 @@
     <permission android:name="android.permission.READ_FRAME_BUFFER"
         android:label="@string/permlab_readFrameBuffer"
         android:description="@string/permdesc_readFrameBuffer"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Required to be able to disable the device (very dangerous!). -->
     <permission android:name="android.permission.BRICK"
@@ -1289,7 +1296,7 @@
     <permission android:name="android.permission.REBOOT"
         android:label="@string/permlab_reboot"
         android:description="@string/permdesc_reboot"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
    <!-- Allows low-level access to power management -->
     <permission android:name="android.permission.DEVICE_POWER"
@@ -1329,7 +1336,7 @@
     <permission android:name="android.permission.MASTER_CLEAR"
         android:label="@string/permlab_masterClear"
         android:description="@string/permdesc_masterClear"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows an application to call any phone number, including emergency
          numbers, without going through the Dialer user interface for the user
@@ -1337,34 +1344,34 @@
     <permission android:name="android.permission.CALL_PRIVILEGED"
         android:label="@string/permlab_callPrivileged"
         android:description="@string/permdesc_callPrivileged"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows an application to perform CDMA OTA provisioning @hide -->
     <permission android:name="android.permission.PERFORM_CDMA_PROVISIONING"
         android:label="@string/permlab_performCdmaProvisioning"
         android:description="@string/permdesc_performCdmaProvisioning"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows enabling/disabling location update notifications from
          the radio. Not for use by normal applications. -->
     <permission android:name="android.permission.CONTROL_LOCATION_UPDATES"
         android:label="@string/permlab_locationUpdates"
         android:description="@string/permdesc_locationUpdates"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows read/write access to the "properties" table in the checkin
          database, to change values that get uploaded. -->
     <permission android:name="android.permission.ACCESS_CHECKIN_PROPERTIES"
         android:label="@string/permlab_checkinProperties"
         android:description="@string/permdesc_checkinProperties"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows an application to collect component usage
          statistics @hide -->
     <permission android:name="android.permission.PACKAGE_USAGE_STATS"
         android:label="@string/permlab_pkgUsageStats"
         android:description="@string/permdesc_pkgUsageStats"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows an application to collect battery statistics -->
     <permission android:name="android.permission.BATTERY_STATS"
@@ -1377,7 +1384,7 @@
     <permission android:name="android.permission.BACKUP"
         android:label="@string/permlab_backup"
         android:description="@string/permdesc_backup"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows a package to launch the secure full-backup confirmation UI.
          ONLY the system process may hold this permission.
@@ -1392,7 +1399,7 @@
     <permission android:name="android.permission.BIND_REMOTEVIEWS"
         android:label="@string/permlab_bindRemoteViews"
         android:description="@string/permdesc_bindRemoteViews"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows an application to tell the AppWidget service which application
          can access AppWidget's data.  The normal user flow is that a user
@@ -1404,7 +1411,7 @@
         android:permissionGroup="android.permission-group.PERSONAL_INFO"
         android:label="@string/permlab_bindGadget"
         android:description="@string/permdesc_bindGadget"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows applications to change the background data setting
          @hide pending API council -->
@@ -1424,7 +1431,7 @@
          besides global search. -->
     <permission android:name="android.permission.GLOBAL_SEARCH"
         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Internal permission protecting access to the global search
          system: ensures that only the system can access the provider
@@ -1442,14 +1449,14 @@
          own apk as Ghod Intended. -->
     <permission android:name="android.permission.SET_WALLPAPER_COMPONENT"
         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allow an application to read and write the cache partition.
          @hide -->
     <permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM"
         android:label="@string/permlab_cache_filesystem"
         android:description="@string/permdesc_cache_filesystem"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Must be required by default container service so that only
          the system can bind to it and use it to copy
@@ -1465,14 +1472,14 @@
         @hide
     -->
     <permission android:name="android.permission.CRYPT_KEEPER"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows an application to read historical network usage for
          specific networks and applications. @hide -->
     <permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY"
         android:label="@string/permlab_readNetworkUsageHistory"
         android:description="@string/permdesc_readNetworkUsageHistory"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows an application to manage network policies (such as warning and disable
          limits) and to define application-specific rules. @hide -->
@@ -1487,7 +1494,7 @@
     <permission android:name="android.permission.MODIFY_NETWORK_ACCOUNTING"
         android:label="@string/permlab_modifyNetworkAccounting"
         android:description="@string/permdesc_modifyNetworkAccounting"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- C2DM permission.
          @hide Used internally.
@@ -1503,7 +1510,7 @@
     <permission android:name="android.permission.PACKAGE_VERIFICATION_AGENT"
         android:label="@string/permlab_packageVerificationAgent"
         android:description="@string/permdesc_packageVerificationAgent"
-        android:protectionLevel="signatureOrSystem" />
+        android:protectionLevel="signature|system" />
 
     <!-- Must be required by package verifier receiver, to ensure that only the
          system can interact with it.
diff --git a/core/res/res/layout/app_perms_summary.xml b/core/res/res/layout/app_perms_summary.xml
index 3f99dde..77dbc2e 100755
--- a/core/res/res/layout/app_perms_summary.xml
+++ b/core/res/res/layout/app_perms_summary.xml
@@ -32,6 +32,15 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content" />
 
+    <!-- List view containing list of new permissions categorized by groups. -->
+    <LinearLayout
+        android:id="@+id/new_perms_list"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:paddingLeft="16dip"
+        android:paddingRight="12dip"
+        android:layout_height="wrap_content" />
+
     <!-- List view containing list of dangerous permissions categorized by groups. -->
     <LinearLayout
         android:id="@+id/dangerous_perms_list"
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 92c59ab..4aa7dde 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -157,7 +157,7 @@
              of permission to a requesting application at installation, without
              asking for the user's explicit approval (though the user always
              has the option to review these permissions before installing). -->
-        <enum name="normal" value="0" />
+        <flag name="normal" value="0" />
         <!-- A higher-risk permission that would give a requesting application
              access to private user data or control over the device that can
              negatively impact the user.  Because this type of permission
@@ -167,13 +167,13 @@
              user and require confirmation before proceeding, or some other
              approach may be taken to avoid the user automatically allowing
              the use of such facilities.  -->
-        <enum name="dangerous" value="1" />
+        <flag name="dangerous" value="1" />
         <!-- A permission that the system is to grant only if the requesting
              application is signed with the same certificate as the application
              that declared the permission. If the certificates match, the system
              automatically grants the permission without notifying the user or
              asking for the user's explicit approval. -->
-        <enum name="signature" value="2" />
+        <flag name="signature" value="2" />
         <!-- A permission that the system is to grant only to packages in the
              Android system image <em>or</em> that are signed with the same
              certificates. Please avoid using this option, as the
@@ -183,7 +183,20 @@
              vendors have applications built in to a system image which need
              to share specific features explicitly because they are being built
              together. -->
-        <enum name="signatureOrSystem" value="3" />
+        <flag name="signatureOrSystem" value="3" />
+        <!-- Additional flag from base permission type: this permission can also
+             be granted to any applications installed on the system image.
+             Please avoid using this option, as the
+             signature protection level should be sufficient for most needs and
+             works regardless of exactly where applications are installed.  This
+             permission flag is used for certain special situations where multiple
+             vendors have applications built in to a system image which need
+             to share specific features explicitly because they are being built
+             together. -->
+        <flag name="system" value="0x10" />
+        <!-- Additional flag from base permission type: this permission can also
+             (optionally) be granted to development applications. -->
+        <flag name="development" value="0x20" />
     </attr>
     
     <!-- Specified the name of a group that this permission is associated
@@ -924,6 +937,13 @@
         tag; often this is one of the {@link android.Manifest.permission standard
         system permissions}. -->
         <attr name="name" />
+        <!--  Specify whether this permission is required for the application.
+              The default is true, meaning the application requires the
+              permission, and it must always be granted when it is installed.
+              If you set this to false, then in some cases the application may
+              be installed with it being granted the permission, and it will
+              need to request the permission later if it needs it. -->
+        <attr name="required" format="boolean" />
     </declare-styleable>
 
     <!-- The <code>uses-configuration</code> tag specifies
@@ -966,7 +986,7 @@
               don't support it.  If you set this to false, then this will
               not impose a restriction on where the application can be
               installed. -->
-        <attr name="required" format="boolean" />
+        <attr name="required" />
     </declare-styleable>
 
     <!-- The <code>uses-sdk</code> tag describes the SDK features that the
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 0950bdb..94a671d 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -115,6 +115,7 @@
   <java-symbol type="id" name="new_app_action" />
   <java-symbol type="id" name="new_app_description" />
   <java-symbol type="id" name="new_app_icon" />
+  <java-symbol type="id" name="new_perms_list" />
   <java-symbol type="id" name="no_permissions" />
   <java-symbol type="id" name="non_dangerous_perms_list" />
   <java-symbol type="id" name="numberpicker_input" />
@@ -629,6 +630,7 @@
   <java-symbol type="string" name="orgTypeWork" />
   <java-symbol type="string" name="passwordIncorrect" />
   <java-symbol type="string" name="permissions_format" />
+  <java-symbol type="string" name="perms_new_perm_prefix" />
   <java-symbol type="string" name="perms_hide" />
   <java-symbol type="string" name="perms_show_all" />
   <java-symbol type="string" name="petabyteShort" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 3c1f50d..f548165 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -861,6 +861,14 @@
         possible to get app components into an unusable, inconsistent, or unstable state.
     </string>
 
+    <!-- Title of an application permission for granting or revoking other permissions [CHAR LIMIT=NONE] -->
+    <string name="permlab_grantRevokePermissions">grant or revoke permissions</string>
+    <!-- Description of an application permission for granting or revoking other permissions [CHAR LIMIT=NONE] -->
+    <string name="permdesc_grantRevokePermissions">Allows an application to grant or revoke
+        specific permissions for it or other applications.  Malicious applications may use this
+        to access features you have not granted them.
+    </string>
+
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_setPreferredApplications">set preferred apps</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -2775,6 +2783,8 @@
     <string name="default_permission_group">Default</string>
     <!-- Do not translate. -->
     <string name="permissions_format"><xliff:g id="perm_line1">%1$s</xliff:g>, <xliff:g id="perm_line2">%2$s</xliff:g></string>
+    <!-- Text that is placed at the front of a permission name that is being added to an app [CHAR LIMIT=NONE] -->
+    <string name="perms_new_perm_prefix"><font size="12" fgcolor="#ffffa3a3">NEW: </font></string>
     <!-- Shown for an application when it doesn't require any permission grants. -->
     <string name="no_permissions">No permissions required</string>
     <!-- When installing an application, the less-dangerous permissions are hidden.  If the user showed those, this is the text to hide them again.  -->
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 7169015..e471a3f 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -1127,7 +1127,10 @@
                     + "; regranting permissions for internal storage");
             mSettings.mInternalSdkPlatform = mSdkVersion;
             
-            updatePermissionsLPw(null, null, true, regrantPermissions, regrantPermissions);
+            updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
+                    | (regrantPermissions
+                            ? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL)
+                            : 0));
 
             // can downgrade to reader
             mSettings.writeLPr();
@@ -1473,7 +1476,7 @@
     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.
-            return PackageParser.generatePackageInfo(p, null, flags, 0, 0);
+            return PackageParser.generatePackageInfo(p, null, flags, 0, 0, null);
         }
         final PackageSetting ps = (PackageSetting)p.mExtras;
         if (ps == null) {
@@ -1481,7 +1484,7 @@
         }
         final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
         return PackageParser.generatePackageInfo(p, gp.gids, flags,
-                ps.firstInstallTime, ps.lastUpdateTime);
+                ps.firstInstallTime, ps.lastUpdateTime, gp.grantedPermissions);
     }
 
     public PackageInfo getPackageInfo(String packageName, int flags) {
@@ -1934,6 +1937,7 @@
         BasePermission bp = mSettings.mPermissions.get(info.name);
         boolean added = bp == null;
         boolean changed = true;
+        int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel);
         if (added) {
             bp = new BasePermission(info.name, tree.sourcePackage,
                     BasePermission.TYPE_DYNAMIC);
@@ -1942,16 +1946,17 @@
                     "Not allowed to modify non-dynamic permission "
                     + info.name);
         } else {
-            if (bp.protectionLevel == info.protectionLevel
+            if (bp.protectionLevel == fixedLevel
                     && bp.perm.owner.equals(tree.perm.owner)
                     && bp.uid == tree.uid
                     && comparePermissionInfos(bp.perm.info, info)) {
                 changed = false;
             }
         }
-        bp.protectionLevel = info.protectionLevel;
-        bp.perm = new PackageParser.Permission(tree.perm.owner,
-                new PermissionInfo(info));
+        bp.protectionLevel = fixedLevel;
+        info = new PermissionInfo(info);
+        info.protectionLevel = fixedLevel;
+        bp.perm = new PackageParser.Permission(tree.perm.owner, info);
         bp.perm.info.packageName = tree.perm.info.packageName;
         bp.uid = tree.uid;
         if (added) {
@@ -1995,6 +2000,77 @@
         }
     }
 
+    public void grantPermission(String packageName, String permissionName) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, null);
+        synchronized (mPackages) {
+            final PackageParser.Package pkg = mPackages.get(packageName);
+            if (pkg == null) {
+                throw new IllegalArgumentException("Unknown package: " + packageName);
+            }
+            final BasePermission bp = mSettings.mPermissions.get(permissionName);
+            if (bp == null) {
+                throw new IllegalArgumentException("Unknown permission: " + packageName);
+            }
+            if (!pkg.requestedPermissions.contains(permissionName)) {
+                throw new SecurityException("Package " + packageName
+                        + " has not requested permission " + permissionName);
+            }
+            if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) == 0) {
+                throw new SecurityException("Permission " + permissionName
+                        + " is not a development permission");
+            }
+            final PackageSetting ps = (PackageSetting) pkg.mExtras;
+            if (ps == null) {
+                return;
+            }
+            final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
+            if (gp.grantedPermissions.add(permissionName)) {
+                if (ps.haveGids) {
+                    gp.gids = appendInts(gp.gids, bp.gids);
+                }
+                mSettings.writeLPr();
+            }
+        }
+    }
+
+    public void revokePermission(String packageName, String permissionName) {
+        synchronized (mPackages) {
+            final PackageParser.Package pkg = mPackages.get(packageName);
+            if (pkg == null) {
+                throw new IllegalArgumentException("Unknown package: " + packageName);
+            }
+            if (pkg.applicationInfo.uid != Binder.getCallingUid()) {
+                mContext.enforceCallingOrSelfPermission(
+                        android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, null);
+            }
+            final BasePermission bp = mSettings.mPermissions.get(permissionName);
+            if (bp == null) {
+                throw new IllegalArgumentException("Unknown permission: " + packageName);
+            }
+            if (!pkg.requestedPermissions.contains(permissionName)) {
+                throw new SecurityException("Package " + packageName
+                        + " has not requested permission " + permissionName);
+            }
+            if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) == 0) {
+                throw new SecurityException("Permission " + permissionName
+                        + " is not a development permission");
+            }
+            final PackageSetting ps = (PackageSetting) pkg.mExtras;
+            if (ps == null) {
+                return;
+            }
+            final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
+            if (gp.grantedPermissions.remove(permissionName)) {
+                gp.grantedPermissions.remove(permissionName);
+                if (ps.haveGids) {
+                    gp.gids = removeInts(gp.gids, bp.gids);
+                }
+                mSettings.writeLPr();
+            }
+        }
+    }
+
     public boolean isProtectedBroadcast(String actionName) {
         synchronized (mPackages) {
             return mProtectedBroadcasts.contains(actionName);
@@ -4117,10 +4193,13 @@
         }
         return false;
     }
-    
+
+    static final int UPDATE_PERMISSIONS_ALL = 1<<0;
+    static final int UPDATE_PERMISSIONS_REPLACE_PKG = 1<<1;
+    static final int UPDATE_PERMISSIONS_REPLACE_ALL = 1<<2;
+
     private void updatePermissionsLPw(String changingPkg,
-            PackageParser.Package pkgInfo, boolean grantPermissions,
-            boolean replace, boolean replaceAll) {
+            PackageParser.Package pkgInfo, int flags) {
         // Make sure there are no dangling permission trees.
         Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator();
         while (it.hasNext()) {
@@ -4138,7 +4217,7 @@
                 if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) {
                     Slog.i(TAG, "Removing old permission tree: " + bp.name
                             + " from package " + bp.sourcePackage);
-                    grantPermissions = true;
+                    flags |= UPDATE_PERMISSIONS_ALL;
                     it.remove();
                 }
             }
@@ -4178,7 +4257,7 @@
                 if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) {
                     Slog.i(TAG, "Removing old permission: " + bp.name
                             + " from package " + bp.sourcePackage);
-                    grantPermissions = true;
+                    flags |= UPDATE_PERMISSIONS_ALL;
                     it.remove();
                 }
             }
@@ -4186,16 +4265,16 @@
 
         // Now update the permissions for all packages, in particular
         // replace the granted permissions of the system packages.
-        if (grantPermissions) {
+        if ((flags&UPDATE_PERMISSIONS_ALL) != 0) {
             for (PackageParser.Package pkg : mPackages.values()) {
                 if (pkg != pkgInfo) {
-                    grantPermissionsLPw(pkg, replaceAll);
+                    grantPermissionsLPw(pkg, (flags&UPDATE_PERMISSIONS_REPLACE_ALL) != 0);
                 }
             }
         }
         
         if (pkgInfo != null) {
-            grantPermissionsLPw(pkgInfo, replace);
+            grantPermissionsLPw(pkgInfo, (flags&UPDATE_PERMISSIONS_REPLACE_PKG) != 0);
         }
     }
 
@@ -4205,11 +4284,13 @@
             return;
         }
         final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
+        HashSet<String> origPermissions = gp.grantedPermissions;
         boolean changedPermission = false;
 
         if (replace) {
             ps.permissionsFixed = false;
             if (gp == ps) {
+                origPermissions = new HashSet<String>(gp.grantedPermissions);
                 gp.grantedPermissions.clear();
                 gp.gids = mGlobalGids;
             }
@@ -4222,6 +4303,7 @@
         final int N = pkg.requestedPermissions.size();
         for (int i=0; i<N; i++) {
             final String name = pkg.requestedPermissions.get(i);
+            //final boolean required = pkg.requestedPermssionsRequired.get(i);
             final BasePermission bp = mSettings.mPermissions.get(name);
             if (DEBUG_INSTALL) {
                 if (gp != ps) {
@@ -4232,23 +4314,23 @@
                 final String perm = bp.name;
                 boolean allowed;
                 boolean allowedSig = false;
-                if (bp.protectionLevel == PermissionInfo.PROTECTION_NORMAL
-                        || bp.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS) {
+                final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
+                if (level == PermissionInfo.PROTECTION_NORMAL
+                        || level == PermissionInfo.PROTECTION_DANGEROUS) {
                     allowed = true;
                 } else if (bp.packageSetting == null) {
                     // This permission is invalid; skip it.
                     allowed = false;
-                } else if (bp.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE
-                        || bp.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) {
+                } else if (level == PermissionInfo.PROTECTION_SIGNATURE) {
                     allowed = (compareSignatures(
                             bp.packageSetting.signatures.mSignatures, pkg.mSignatures)
                                     == PackageManager.SIGNATURE_MATCH)
                             || (compareSignatures(mPlatformPackage.mSignatures, pkg.mSignatures)
                                     == PackageManager.SIGNATURE_MATCH);
-                    if (!allowed && bp.protectionLevel
-                            == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) {
+                    if (!allowed && (bp.protectionLevel
+                            & PermissionInfo.PROTECTION_FLAG_SYSTEM) != 0) {
                         if (isSystemApp(pkg)) {
-                            // For updated system applications, the signatureOrSystem permission
+                            // For updated system applications, a system permission
                             // is granted only if it had been defined by the original application.
                             if (isUpdatedSystemApp(pkg)) {
                                 final PackageSetting sysPs = mSettings
@@ -4265,6 +4347,16 @@
                             }
                         }
                     }
+                    if (!allowed && (bp.protectionLevel
+                            & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) {
+                        // For development permissions, a development permission
+                        // is granted only if it was already granted.
+                        if (origPermissions.contains(perm)) {
+                            allowed = true;
+                        } else {
+                            allowed = false;
+                        }
+                    }
                     if (allowed) {
                         allowedSig = true;
                     }
@@ -4883,7 +4975,7 @@
                             // writer
                             synchronized (mPackages) {
                                 updatePermissionsLPw(p.packageName, p,
-                                        p.permissions.size() > 0, false, false);
+                                        p.permissions.size() > 0 ? UPDATE_PERMISSIONS_ALL : 0);
                             }
                             addedPackage = p.applicationInfo.packageName;
                             addedUid = p.applicationInfo.uid;
@@ -6447,7 +6539,7 @@
                 // writer
                 synchronized (mPackages) {
                     updatePermissionsLPw(deletedPackage.packageName, deletedPackage,
-                            true, false, false);
+                            UPDATE_PERMISSIONS_ALL);
                     // can downgrade to reader
                     mSettings.writeLPr();
                 }
@@ -6591,7 +6683,8 @@
         }
         synchronized (mPackages) {
             updatePermissionsLPw(newPackage.packageName, newPackage,
-                    newPackage.permissions.size() > 0, true, false);
+                    UPDATE_PERMISSIONS_REPLACE_PKG | (newPackage.permissions.size() > 0
+                            ? UPDATE_PERMISSIONS_ALL : 0));
             res.name = pkgName;
             res.uid = newPackage.applicationInfo.uid;
             res.pkg = newPackage;
@@ -7019,7 +7112,7 @@
                         outInfo.removedUid = mSettings.removePackageLPw(packageName);
                     }
                     if (deletedPs != null) {
-                        updatePermissionsLPw(deletedPs.name, null, false, false, false);
+                        updatePermissionsLPw(deletedPs.name, null, 0);
                         if (deletedPs.sharedUser != null) {
                             // remove permissions associated with package
                             mSettings.updateSharedUserPermsLPw(deletedPs, mGlobalGids);
@@ -7102,7 +7195,8 @@
         }
         // writer
         synchronized (mPackages) {
-            updatePermissionsLPw(newPkg.packageName, newPkg, true, true, false);
+            updatePermissionsLPw(newPkg.packageName, newPkg,
+                    UPDATE_PERMISSIONS_ALL | UPDATE_PERMISSIONS_REPLACE_PKG);
             // can downgrade to reader here
             if (writeSettings) {
                 mSettings.writeLPr();
@@ -8320,7 +8414,10 @@
 
             // Make sure group IDs have been assigned, and any permission
             // changes in other apps are accounted for
-            updatePermissionsLPw(null, null, true, regrantPermissions, regrantPermissions);
+            updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
+                    | (regrantPermissions
+                            ? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL)
+                            : 0));
             // can downgrade to reader
             // Persist settings
             mSettings.writeLPr();
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index ebf954b..5da6ac9 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -1398,6 +1398,7 @@
                             dynamic ? BasePermission.TYPE_DYNAMIC : BasePermission.TYPE_NORMAL);
                     bp.protectionLevel = readInt(parser, null, "protection",
                             PermissionInfo.PROTECTION_NORMAL);
+                    bp.protectionLevel = PermissionInfo.fixProtectionLevel(bp.protectionLevel);
                     if (dynamic) {
                         PermissionInfo pi = new PermissionInfo();
                         pi.packageName = sourcePackage.intern();
@@ -2244,7 +2245,8 @@
             pw.print("    uid="); pw.print(p.uid);
                     pw.print(" gids="); pw.print(PackageManagerService.arrayToString(p.gids));
                     pw.print(" type="); pw.print(p.type);
-                    pw.print(" prot="); pw.println(p.protectionLevel);
+                    pw.print(" prot=");
+                    pw.println(PermissionInfo.protectionToString(p.protectionLevel));
             if (p.packageSetting != null) {
                 pw.print("    packageSetting="); pw.println(p.packageSetting);
             }
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 58680ea..351c771 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -159,6 +159,18 @@
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
+    @Override
+    public void grantPermission(String packageName, String permissionName) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** @hide */
+    @Override
+    public void revokePermission(String packageName, String permissionName) {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public int checkSignatures(String pkg1, String pkg2) {
         throw new UnsupportedOperationException();