New permissions UI.

Had to keep around a little of the old API for the device
admin settings UI to continue to use.

Change-Id: I84c5666a7f3fec0495eae196a0ffc2223c9e6f82
diff --git a/api/current.txt b/api/current.txt
index 22bc7f2..f5a75a0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5783,7 +5783,7 @@
     field public static final java.lang.String CATEGORY_UNIT_TEST = "android.intent.category.UNIT_TEST";
     field public static final android.os.Parcelable.Creator CREATOR;
     field public static final java.lang.String EXTRA_ALARM_COUNT = "android.intent.extra.ALARM_COUNT";
-    field public static final java.lang.String EXTRA_ALLOW_REPLACE = "android.intent.extra.ALLOW_REPLACE";
+    field public static final deprecated java.lang.String EXTRA_ALLOW_REPLACE = "android.intent.extra.ALLOW_REPLACE";
     field public static final java.lang.String EXTRA_BCC = "android.intent.extra.BCC";
     field public static final java.lang.String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT";
     field public static final java.lang.String EXTRA_CC = "android.intent.extra.CC";
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index b4669a7..cb8fea21 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1240,7 +1240,11 @@
      * Used as a boolean extra field with {@link #ACTION_INSTALL_PACKAGE} to install a
      * package.  Tells the installer UI to skip the confirmation with the user
      * if the .apk is replacing an existing one.
+     * @deprecated As of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}, Android
+     * will no longer show an interstitial message about updating existing
+     * applications so this is no longer needed.
      */
+    @Deprecated
     public static final String EXTRA_ALLOW_REPLACE
             = "android.intent.extra.ALLOW_REPLACE";
 
diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java
index b409e26..5d8bdbb 100755
--- a/core/java/android/widget/AppSecurityPermissions.java
+++ b/core/java/android/widget/AppSecurityPermissions.java
@@ -18,7 +18,9 @@
 
 import com.android.internal.R;
 
+import android.app.AlertDialog;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -27,14 +29,13 @@
 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.AttributeSet;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
 
 import java.text.Collator;
 import java.util.ArrayList;
@@ -42,7 +43,6 @@
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -58,16 +58,51 @@
  * 
  * {@hide}
  */
-public class AppSecurityPermissions  implements View.OnClickListener {
+public class AppSecurityPermissions {
 
-    private enum State {
-        NO_PERMS,
-        DANGEROUS_ONLY,
-        NORMAL_ONLY,
-        BOTH
+    public static final int WHICH_PERSONAL = 1<<0;
+    public static final int WHICH_DEVICE = 1<<1;
+    public static final int WHICH_NEW = 1<<2;
+    public static final int WHICH_ALL = 0xffff;
+
+    private final static String TAG = "AppSecurityPermissions";
+    private boolean localLOGV = false;
+    private Context mContext;
+    private LayoutInflater mInflater;
+    private PackageManager mPm;
+    private PackageInfo mInstalledPackageInfo;
+    private final Map<String, MyPermissionGroupInfo> mPermGroups
+            = new HashMap<String, MyPermissionGroupInfo>();
+    private final List<MyPermissionGroupInfo> mPermGroupsList
+            = new ArrayList<MyPermissionGroupInfo>();
+    private final PermissionGroupInfoComparator mPermGroupComparator;
+    private final PermissionInfoComparator mPermComparator;
+    private List<MyPermissionInfo> mPermsList;
+    private CharSequence mNewPermPrefix;
+    private Drawable mNormalIcon;
+    private Drawable mDangerousIcon;
+
+    static class MyPermissionGroupInfo extends PermissionGroupInfo {
+        CharSequence mLabel;
+
+        final ArrayList<MyPermissionInfo> mNewPermissions = new ArrayList<MyPermissionInfo>();
+        final ArrayList<MyPermissionInfo> mPersonalPermissions = new ArrayList<MyPermissionInfo>();
+        final ArrayList<MyPermissionInfo> mDevicePermissions = new ArrayList<MyPermissionInfo>();
+        final ArrayList<MyPermissionInfo> mAllPermissions = new ArrayList<MyPermissionInfo>();
+
+        MyPermissionGroupInfo(PermissionInfo perm) {
+            name = perm.packageName;
+            packageName = perm.packageName;
+        }
+
+        MyPermissionGroupInfo(PermissionGroupInfo info) {
+            super(info);
+        }
     }
 
     static class MyPermissionInfo extends PermissionInfo {
+        CharSequence mLabel;
+
         /**
          * PackageInfo.requestedPermissionsFlags for the new package being installed.
          */
@@ -99,46 +134,99 @@
         }
     }
 
-    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, 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;
-    private Drawable mShowMaxIcon;
-    private Drawable mShowMinIcon;
-    private View mShowMore;
-    private TextView mShowMoreText;
-    private ImageView mShowMoreIcon;
-    private State mCurrentState;
-    private LinearLayout mNonDangerousList;
-    private LinearLayout mDangerousList;
-    private LinearLayout mNewList;
-    private HashMap<String, CharSequence> mGroupLabelCache;
-    private View mNoPermsView;
+    public static class PermissionItemView extends LinearLayout implements View.OnClickListener {
+        MyPermissionGroupInfo mGroup;
+        MyPermissionInfo mPerm;
+        AlertDialog mDialog;
+
+        public PermissionItemView(Context context, AttributeSet attrs) {
+            super(context, attrs);
+            setClickable(true);
+        }
+
+        public void setPermission(MyPermissionGroupInfo grp, MyPermissionInfo perm,
+                boolean first, CharSequence newPermPrefix) {
+            mGroup = grp;
+            mPerm = perm;
+
+            ImageView permGrpIcon = (ImageView) findViewById(R.id.perm_icon);
+            TextView permNameView = (TextView) findViewById(R.id.perm_name);
+
+            PackageManager pm = getContext().getPackageManager();
+            Drawable icon = null;
+            if (first) {
+                if (grp.icon != 0) {
+                    icon = grp.loadIcon(pm);
+                } else {
+                    ApplicationInfo appInfo;
+                    try {
+                        appInfo = pm.getApplicationInfo(grp.packageName, 0);
+                        icon = appInfo.loadIcon(pm);
+                    } catch (NameNotFoundException e) {
+                    }
+                }
+            }
+            CharSequence label = perm.mLabel;
+            if (perm.mNew && newPermPrefix != null) {
+                // If this is a new permission, format it appropriately.
+                SpannableStringBuilder builder = new SpannableStringBuilder();
+                Parcel parcel = Parcel.obtain();
+                TextUtils.writeToParcel(newPermPrefix, parcel, 0);
+                parcel.setDataPosition(0);
+                CharSequence newStr = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+                parcel.recycle();
+                builder.append(newStr);
+                builder.append(label);
+                label = builder;
+            }
+
+            permGrpIcon.setImageDrawable(icon);
+            permNameView.setText(label);
+            setOnClickListener(this);
+        }
+
+        @Override
+        public void onClick(View v) {
+            if (mGroup != null && mPerm != null) {
+                if (mDialog != null) {
+                    mDialog.dismiss();
+                }
+                AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
+                builder.setTitle(mGroup.mLabel);
+                builder.setMessage(mPerm.loadDescription(getContext().getPackageManager()));
+                builder.setCancelable(true);
+                mDialog = builder.show();
+                mDialog.setCanceledOnTouchOutside(true);
+            }
+        }
+
+        @Override
+        protected void onDetachedFromWindow() {
+            super.onDetachedFromWindow();
+            if (mDialog != null) {
+                mDialog.dismiss();
+            }
+        }
+    }
 
     public AppSecurityPermissions(Context context, List<PermissionInfo> permList) {
         mContext = context;
         mPm = mContext.getPackageManager();
+        loadResources();
+        mPermComparator = new PermissionInfoComparator();
+        mPermGroupComparator = new PermissionGroupInfoComparator();
         for (PermissionInfo pi : permList) {
             mPermsList.add(new MyPermissionInfo(pi));
         }
+        setPermissions(mPermsList);
     }
     
     public AppSecurityPermissions(Context context, String packageName) {
         mContext = context;
         mPm = mContext.getPackageManager();
+        loadResources();
+        mPermComparator = new PermissionInfoComparator();
+        mPermGroupComparator = new PermissionGroupInfoComparator();
         mPermsList = new ArrayList<MyPermissionInfo>();
         Set<MyPermissionInfo> permSet = new HashSet<MyPermissionInfo>();
         PackageInfo pkgInfo;
@@ -155,11 +243,15 @@
         for(MyPermissionInfo tmpInfo : permSet) {
             mPermsList.add(tmpInfo);
         }
+        setPermissions(mPermsList);
     }
-    
+
     public AppSecurityPermissions(Context context, PackageParser.Package pkg) {
         mContext = context;
         mPm = mContext.getPackageManager();
+        loadResources();
+        mPermComparator = new PermissionInfoComparator();
+        mPermGroupComparator = new PermissionGroupInfoComparator();
         mPermsList = new ArrayList<MyPermissionInfo>();
         Set<MyPermissionInfo> permSet = new HashSet<MyPermissionInfo>();
         if(pkg == null) {
@@ -193,10 +285,20 @@
         for (MyPermissionInfo tmpInfo : permSet) {
             mPermsList.add(tmpInfo);
         }
+        setPermissions(mPermsList);
     }
-    
+
+    private void loadResources() {
+        // Pick up from framework resources instead.
+        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);
+    }
+
     /**
-     * Utility to retrieve a view displaying a single permission.
+     * Utility to retrieve a view displaying a single permission.  This provides
+     * the old UI layout for permissions; it is only here for the device admin
+     * settings to continue to use.
      */
     public static View getPermissionItemView(Context context,
             CharSequence grpName, CharSequence description, boolean dangerous) {
@@ -204,10 +306,14 @@
                 Context.LAYOUT_INFLATER_SERVICE);
         Drawable icon = context.getResources().getDrawable(dangerous
                 ? R.drawable.ic_bullet_key_permission : R.drawable.ic_text_dot);
-        return getPermissionItemView(context, inflater, grpName,
+        return getPermissionItemViewOld(context, inflater, grpName,
                 description, dangerous, icon);
     }
     
+    public PackageInfo getInstalledPackageInfo() {
+        return mInstalledPackageInfo;
+    }
+
     private void getAllUsedPermissions(int sharedUid, Set<MyPermissionInfo> permSet) {
         String sharedPkgList[] = mPm.getPackagesForUid(sharedUid);
         if(sharedPkgList == null || (sharedPkgList.length == 0)) {
@@ -239,6 +345,7 @@
         if ((strList == null) || (strList.length == 0)) {
             return;
         }
+        mInstalledPackageInfo = installedPkgInfo;
         for (int i=0; i<strList.length; i++) {
             String permName = strList[i];
             // If we are only looking at an existing app, then we only
@@ -270,13 +377,42 @@
                     // to see, so skip it.
                     continue;
                 }
+                final String origGroupName = tmpPermInfo.group;
+                String groupName = origGroupName;
+                if (groupName == null) {
+                    groupName = tmpPermInfo.packageName;
+                    tmpPermInfo.group = groupName;
+                }
+                MyPermissionGroupInfo group = mPermGroups.get(groupName);
+                if (group == null) {
+                    PermissionGroupInfo grp = null;
+                    if (origGroupName != null) {
+                        grp = mPm.getPermissionGroupInfo(origGroupName, 0);
+                    }
+                    if (grp != null) {
+                        group = new MyPermissionGroupInfo(grp);
+                    } else {
+                        // We could be here either because the permission
+                        // didn't originally specify a group or the group it
+                        // gave couldn't be found.  In either case, we consider
+                        // its group to be the permission's package name.
+                        tmpPermInfo.group = tmpPermInfo.packageName;
+                        group = mPermGroups.get(tmpPermInfo.group);
+                        if (group == null) {
+                            group = new MyPermissionGroupInfo(tmpPermInfo);
+                        }
+                        group = new MyPermissionGroupInfo(tmpPermInfo);
+                    }
+                    mPermGroups.put(tmpPermInfo.group, group);
+                }
+                final boolean newPerm = installedPkgInfo != null
+                        && (existingFlags&PackageInfo.REQUESTED_PERMISSION_GRANTED) == 0;
                 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;
+                myPerm.mNew = newPerm;
                 permSet.add(myPerm);
             } catch (NameNotFoundException e) {
                 Log.i(TAG, "Ignoring unknown permission:"+permName);
@@ -285,149 +421,99 @@
     }
     
     public int getPermissionCount() {
-        return mPermsList.size();
+        return getPermissionCount(WHICH_ALL);
+    }
+
+    private List<MyPermissionInfo> getPermissionList(MyPermissionGroupInfo grp, int which) {
+        if (which == WHICH_NEW) {
+            return grp.mNewPermissions;
+        } else if (which == WHICH_PERSONAL) {
+            return grp.mPersonalPermissions;
+        } else if (which == WHICH_DEVICE) {
+            return grp.mDevicePermissions;
+        } else {
+            return grp.mAllPermissions;
+        }
+    }
+
+    public int getPermissionCount(int which) {
+        int N = 0;
+        for (int i=0; i<mPermGroupsList.size(); i++) {
+            N += getPermissionList(mPermGroupsList.get(i), which).size();
+        }
+        return N;
     }
 
     public View getPermissionsView() {
-        
+        return getPermissionsView(WHICH_ALL);
+    }
+
+    public View getPermissionsView(int which) {
         mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-        mPermsView = (LinearLayout) mInflater.inflate(R.layout.app_perms_summary, null);
-        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);
 
-        // Set up the LinearLayout that acts like a list item.
-        mShowMore.setClickable(true);
-        mShowMore.setOnClickListener(this);
-        mShowMore.setFocusable(true);
+        LinearLayout permsView = (LinearLayout) mInflater.inflate(R.layout.app_perms_summary, null);
+        LinearLayout displayList = (LinearLayout) permsView.findViewById(R.id.perms_list);
+        View noPermsView = permsView.findViewById(R.id.no_permissions);
 
-        // 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);
-        mShowMinIcon = mContext.getResources().getDrawable(R.drawable.expander_open_holo_dark);
-        
-        // Set permissions view
-        setPermissions(mPermsList);
-        return mPermsView;
-    }
+        displayPermissions(mPermGroupsList, displayList, which);
+        if (displayList.getChildCount() <= 0) {
+            noPermsView.setVisibility(View.VISIBLE);
+        }
 
-    /**
-     * 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 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());
-    }
-
-    private CharSequence getGroupLabel(String grpName) {
-        if (grpName == null) {
-            //return default label
-            return mDefaultGrpLabel;
-        }
-        CharSequence cachedLabel = mGroupLabelCache.get(grpName);
-        if (cachedLabel != null) {
-            return cachedLabel;
-        }
-        PermissionGroupInfo pgi;
-        try {
-            pgi = mPm.getPermissionGroupInfo(grpName, 0);
-        } catch (NameNotFoundException e) {
-            Log.i(TAG, "Invalid group name:" + grpName);
-            return null;
-        }
-        CharSequence label = pgi.loadLabel(mPm).toString();
-        mGroupLabelCache.put(grpName, label);
-        return label;
+        return permsView;
     }
 
     /**
      * Utility method that displays permissions from a map containing group name and
      * list of permission descriptions.
      */
-    private void displayPermissions(Map<String, CharSequence> permInfoMap,
-            LinearLayout permListView, boolean dangerous) {
+    private void displayPermissions(List<MyPermissionGroupInfo> groups,
+            LinearLayout permListView, int which) {
         permListView.removeAllViews();
 
-        Set<String> permInfoStrSet = permInfoMap.keySet();
-        for (String loopPermGrpInfoStr : permInfoStrSet) {
-            CharSequence grpLabel = getGroupLabel(loopPermGrpInfoStr);
-            //guaranteed that grpLabel wont be null since permissions without groups
-            //will belong to the default group
-            if(localLOGV) Log.i(TAG, "Adding view group:" + grpLabel + ", desc:"
-                    + permInfoMap.get(loopPermGrpInfoStr));
-            permListView.addView(getPermissionItemView(grpLabel,
-                    permInfoMap.get(loopPermGrpInfoStr), dangerous));
+        int spacing = (int)(8*mContext.getResources().getDisplayMetrics().density);
+
+        for (int i=0; i<groups.size(); i++) {
+            MyPermissionGroupInfo grp = groups.get(i);
+            final List<MyPermissionInfo> perms = getPermissionList(grp, which);
+            for (int j=0; j<perms.size(); j++) {
+                MyPermissionInfo perm = perms.get(j);
+                View view = getPermissionItemView(grp, perm, j == 0,
+                        which != WHICH_NEW ? mNewPermPrefix : null);
+                LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+                        ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.WRAP_CONTENT);
+                if (j == 0) {
+                    lp.topMargin = spacing;
+                }
+                if (j == grp.mAllPermissions.size()-1) {
+                    lp.bottomMargin = spacing;
+                }
+                if (permListView.getChildCount() == 0) {
+                    lp.topMargin *= 2;
+                }
+                permListView.addView(view, lp);
+            }
         }
     }
 
-    private void displayNoPermissions() {
-        mNoPermsView.setVisibility(View.VISIBLE);
+    private PermissionItemView getPermissionItemView(MyPermissionGroupInfo grp,
+            MyPermissionInfo perm, boolean first, CharSequence newPermPrefix) {
+        return getPermissionItemView(mContext, mInflater, grp, perm, first, newPermPrefix);
     }
 
-    private View getPermissionItemView(CharSequence grpName, CharSequence permList,
-            boolean dangerous) {
-        return getPermissionItemView(mContext, mInflater, grpName, permList,
-                dangerous, dangerous ? mDangerousIcon : mNormalIcon);
+    private static PermissionItemView getPermissionItemView(Context context, LayoutInflater inflater,
+            MyPermissionGroupInfo grp, MyPermissionInfo perm, boolean first,
+            CharSequence newPermPrefix) {
+        PermissionItemView permView = (PermissionItemView)inflater.inflate(
+                R.layout.app_permission_item, null);
+        permView.setPermission(grp, perm, first, newPermPrefix);
+        return permView;
     }
 
-    private static View getPermissionItemView(Context context, LayoutInflater inflater,
+    private static View getPermissionItemViewOld(Context context, LayoutInflater inflater,
             CharSequence grpName, CharSequence permList, boolean dangerous, Drawable icon) {
-        View permView = inflater.inflate(R.layout.app_permission_item, null);
+        View permView = inflater.inflate(R.layout.app_permission_item_old, null);
 
         TextView permGrpView = (TextView) permView.findViewById(R.id.permission_group);
         TextView permDescView = (TextView) permView.findViewById(R.id.permission_list);
@@ -444,41 +530,6 @@
         return permView;
     }
 
-    private void showPermissions() {
-
-        switch(mCurrentState) {
-        case NO_PERMS:
-            displayNoPermissions();
-            break;
-
-        case DANGEROUS_ONLY:
-            displayPermissions(mNewMap, mNewList, true);
-            displayPermissions(mDangerousMap, mDangerousList, true);
-            break;
-
-        case NORMAL_ONLY:
-            displayPermissions(mNewMap, mNewList, true);
-            displayPermissions(mNormalMap, mNonDangerousList, false);
-            break;
-
-        case BOTH:
-            displayPermissions(mNewMap, mNewList, true);
-            displayPermissions(mDangerousMap, mDangerousList, true);
-            if (mExpanded) {
-                displayPermissions(mNormalMap, mNonDangerousList, false);
-                mShowMoreIcon.setImageDrawable(mShowMaxIcon);
-                mShowMoreText.setText(R.string.perms_hide);
-                mNonDangerousList.setVisibility(View.VISIBLE);
-            } else {
-                mShowMoreIcon.setImageDrawable(mShowMinIcon);
-                mShowMoreText.setText(R.string.perms_show_all);
-                mNonDangerousList.setVisibility(View.GONE);
-            }
-            mShowMore.setVisibility(View.VISIBLE);
-            break;
-        }
-    }
-
     private boolean isDisplayablePermission(PermissionInfo pInfo, int newReqFlags,
             int existingReqFlags) {
         final int base = pInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
@@ -496,79 +547,45 @@
         }
         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<MyPermissionInfo> > map,
-            Map<String, CharSequence> retMap, boolean newPerms) {
-        if(map == null) {
-            return;
-        }
-        if(retMap == null) {
-           return;
-        }
-        Set<String> grpNames = map.keySet();
-        Iterator<String> grpNamesIter = grpNames.iterator();
-        while(grpNamesIter.hasNext()) {
-            CharSequence grpDesc = null;
-            String grpNameKey = grpNamesIter.next();
-            List<MyPermissionInfo> grpPermsList = map.get(grpNameKey);
-            if(grpPermsList == null) {
-                continue;
-            }
-            for(PermissionInfo permInfo: grpPermsList) {
-                CharSequence permDesc = permInfo.loadLabel(mPm);
-                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);
-            }
-        }
-    }
     
-    private static class PermissionInfoComparator implements Comparator<PermissionInfo> {
-        private PackageManager mPm;
+    private static class PermissionGroupInfoComparator implements Comparator<MyPermissionGroupInfo> {
         private final Collator sCollator = Collator.getInstance();
-        PermissionInfoComparator(PackageManager pm) {
-            mPm = pm;
+        PermissionGroupInfoComparator() {
         }
-        public final int compare(PermissionInfo a, PermissionInfo b) {
-            CharSequence sa = a.loadLabel(mPm);
-            CharSequence sb = b.loadLabel(mPm);
-            return sCollator.compare(sa, sb);
+        public final int compare(MyPermissionGroupInfo a, MyPermissionGroupInfo b) {
+            if (((a.flags^b.flags)&PermissionGroupInfo.FLAG_PERSONAL_INFO) != 0) {
+                return ((a.flags&PermissionGroupInfo.FLAG_PERSONAL_INFO) != 0) ? -1 : 1;
+            }
+            if (a.priority != b.priority) {
+                return a.priority > b.priority ? -1 : 1;
+            }
+            return sCollator.compare(a.mLabel, b.mLabel);
         }
     }
     
+    private static class PermissionInfoComparator implements Comparator<MyPermissionInfo> {
+        private final Collator sCollator = Collator.getInstance();
+        PermissionInfoComparator() {
+        }
+        public final int compare(MyPermissionInfo a, MyPermissionInfo b) {
+            return sCollator.compare(a.mLabel, b.mLabel);
+        }
+    }
+
+    private void addPermToList(List<MyPermissionInfo> permList,
+            MyPermissionInfo pInfo) {
+        if (pInfo.mLabel == null) {
+            pInfo.mLabel = pInfo.loadLabel(mPm);
+        }
+        int idx = Collections.binarySearch(permList, pInfo, mPermComparator);
+        if(localLOGV) Log.i(TAG, "idx="+idx+", list.size="+permList.size());
+        if (idx < 0) {
+            idx = -idx-1;
+            permList.add(idx, pInfo);
+        }
+    }
+
     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, CharSequence>();
-        // Map containing group names and a list of permissions under that group
-        // categorized as normal
-        mNormalMap = new HashMap<String, CharSequence>();
-        
-        // Additional structures needed to ensure that permissions are unique under 
-        // each group
-        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 (MyPermissionInfo pInfo : permList) {
@@ -577,51 +594,26 @@
                     if(localLOGV) Log.i(TAG, "Permission:"+pInfo.name+" is not displayable");
                     continue;
                 }
-                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<MyPermissionInfo> grpPermsList = permInfoMap.get(grpName);
-                if(grpPermsList == null) {
-                    grpPermsList = new ArrayList<MyPermissionInfo>();
-                    permInfoMap.put(grpName, grpPermsList);
-                    grpPermsList.add(pInfo);
-                } else {
-                    int idx = Collections.binarySearch(grpPermsList, pInfo, permComparator);
-                    if(localLOGV) Log.i(TAG, "idx="+idx+", list.size="+grpPermsList.size());
-                    if (idx < 0) {
-                        idx = -idx-1;
-                        grpPermsList.add(idx, pInfo);
+                MyPermissionGroupInfo group = mPermGroups.get(pInfo.group);
+                if (group != null) {
+                    pInfo.mLabel = pInfo.loadLabel(mPm);
+                    addPermToList(group.mAllPermissions, pInfo);
+                    if (pInfo.mNew) {
+                        addPermToList(group.mNewPermissions, pInfo);
+                    }
+                    if ((group.flags&PermissionGroupInfo.FLAG_PERSONAL_INFO) != 0) {
+                        addPermToList(group.mPersonalPermissions, pInfo);
+                    } else {
+                        addPermToList(group.mDevicePermissions, pInfo);
                     }
                 }
             }
-            // Second pass to actually form the descriptions
-            // Look at dangerous permissions first
-            aggregateGroupDescs(newMap, mNewMap, true);
-            aggregateGroupDescs(dangerousMap, mDangerousMap, false);
-            aggregateGroupDescs(normalMap, mNormalMap, false);
         }
 
-        mCurrentState = State.NO_PERMS;
-        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;
+        for (MyPermissionGroupInfo pgrp : mPermGroups.values()) {
+            pgrp.mLabel = pgrp.loadLabel(mPm);
+            mPermGroupsList.add(pgrp);
         }
-        if(localLOGV) Log.i(TAG, "mCurrentState=" + mCurrentState);
-        showPermissions();
-    }
-
-    public void onClick(View v) {
-        if(localLOGV) Log.i(TAG, "mExpanded="+mExpanded);
-        mExpanded = !mExpanded;
-        showPermissions();
+        Collections.sort(mPermGroupsList, mPermGroupComparator);
     }
 }
diff --git a/core/res/res/layout/app_permission_item.xml b/core/res/res/layout/app_permission_item.xml
index 1bd267f..9b8c5ae 100644
--- a/core/res/res/layout/app_permission_item.xml
+++ b/core/res/res/layout/app_permission_item.xml
@@ -19,37 +19,32 @@
   Contains the group name and a list of permission labels under the group.
 -->
 
-<RelativeLayout
+<view class="android.widget.AppSecurityPermissions$PermissionItemView"
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content">
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    android:background="?android:attr/selectableItemBackground">
 
     <ImageView
         android:id="@+id/perm_icon"
-        android:layout_width="30dip"
-        android:layout_height="30dip"
-        android:layout_alignParentLeft="true" 
+        android:layout_width="32dp"
+        android:layout_height="32dp"
+        android:layout_marginLeft="16dp"
+        android:layout_marginRight="8dp"
         android:scaleType="fitCenter" />
 
-
-    <TextView
-        android:id="@+id/permission_group"
-        android:textAppearance="?android:attr/textAppearanceMedium"
-        android:textStyle="bold"
-        android:paddingLeft="6dip"
-        android:layout_toRightOf="@id/perm_icon"
+    <ImageView
         android:layout_width="wrap_content"
-        android:layout_height="wrap_content" />
+        android:layout_height="match_parent"
+        android:background="?android:attr/dividerVertical" />
 
     <TextView
-        android:id="@+id/permission_list"
+        android:id="@+id/perm_name"
         android:textAppearance="?android:attr/textAppearanceSmall"
-        android:layout_marginTop="-4dip"
-        android:paddingBottom="8dip"
-        android:paddingLeft="6dip"
-        android:layout_below="@id/permission_group"
-        android:layout_toRightOf="@id/perm_icon"
+        android:layout_marginLeft="8dp"
         android:layout_width="wrap_content"
-        android:layout_height="wrap_content" />
+        android:layout_height="wrap_content"
+        android:layout_gravity="top|left" />
 
-</RelativeLayout>
+</view>
diff --git a/core/res/res/layout/app_permission_item_old.xml b/core/res/res/layout/app_permission_item_old.xml
new file mode 100644
index 0000000..1bd267f
--- /dev/null
+++ b/core/res/res/layout/app_permission_item_old.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!--
+  Defines the layout of a single permission item.
+  Contains the group name and a list of permission labels under the group.
+-->
+
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <ImageView
+        android:id="@+id/perm_icon"
+        android:layout_width="30dip"
+        android:layout_height="30dip"
+        android:layout_alignParentLeft="true" 
+        android:scaleType="fitCenter" />
+
+
+    <TextView
+        android:id="@+id/permission_group"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textStyle="bold"
+        android:paddingLeft="6dip"
+        android:layout_toRightOf="@id/perm_icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+    <TextView
+        android:id="@+id/permission_list"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:layout_marginTop="-4dip"
+        android:paddingBottom="8dip"
+        android:paddingLeft="6dip"
+        android:layout_below="@id/permission_group"
+        android:layout_toRightOf="@id/perm_icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+</RelativeLayout>
diff --git a/core/res/res/layout/app_perms_summary.xml b/core/res/res/layout/app_perms_summary.xml
index 77dbc2e..509c502 100755
--- a/core/res/res/layout/app_perms_summary.xml
+++ b/core/res/res/layout/app_perms_summary.xml
@@ -26,88 +26,17 @@
         android:id="@+id/no_permissions"
         android:text="@string/no_permissions"
         android:textAppearance="?android:attr/textAppearanceMedium"
-        android:paddingLeft="16dip"
-        android:paddingRight="12dip"
+        android:paddingLeft="8dp"
+        android:paddingRight="8dp"
         android:visibility="gone"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content" />
 
-    <!-- List view containing list of new permissions categorized by groups. -->
+    <!-- Populated with all permissions. -->
     <LinearLayout
-        android:id="@+id/new_perms_list"
+        android:id="@+id/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"
-        android:orientation="vertical"
-        android:layout_width="match_parent"
-        android:paddingLeft="16dip"
-        android:paddingRight="12dip"
-        android:layout_height="wrap_content" />
-
-    <!-- Clickable area letting user display additional permissions. -->
-    <LinearLayout
-        android:id="@+id/show_more"
-        android:orientation="vertical"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:visibility="gone"
-        android:layout_marginTop="12dip"
-        android:layout_marginBottom="16dip">
-
-        <View
-            android:layout_width="match_parent"
-            android:layout_height="1dip"
-            android:background="?android:attr/listDivider" />
-
-        <LinearLayout
-            android:orientation="horizontal"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:paddingTop="16dip"
-            android:paddingBottom="12dip"
-            android:paddingLeft="16dip"
-            android:duplicateParentState="true"
-            android:background="?android:attr/selectableItemBackground">
-
-            <TextView
-                android:id="@+id/show_more_text"
-                android:textAppearance="?android:attr/textAppearanceMedium"
-                android:duplicateParentState="true"
-                android:layout_alignTop="@+id/show_more_icon"
-                android:layout_gravity="center_vertical"
-                android:paddingLeft="36dip"
-                android:layout_weight="1"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content" />
-
-            <ImageView
-                android:id="@id/show_more_icon"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginRight="12dip" />
-
-        </LinearLayout>
-
-        <View
-            android:layout_width="match_parent"
-            android:layout_height="1dip"
-            android:background="?android:attr/listDivider" />
-
-    </LinearLayout>
-
-    <!-- List view containing list of permissions that aren't dangerous. -->
-    <LinearLayout
-        android:id="@+id/non_dangerous_perms_list"
-        android:orientation="vertical"
-        android:paddingLeft="16dip"
-        android:paddingRight="12dip"
-        android:layout_width="match_parent"
         android:layout_height="wrap_content" />
 
 </LinearLayout>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 978e2a8..2387e7a 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -63,7 +63,6 @@
   <java-symbol type="id" name="clearDefaultHint" />
   <java-symbol type="id" name="contentPanel" />
   <java-symbol type="id" name="customPanel" />
-  <java-symbol type="id" name="dangerous_perms_list" />
   <java-symbol type="id" name="datePicker" />
   <java-symbol type="id" name="day" />
   <java-symbol type="id" name="day_names" />
@@ -117,9 +116,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" />
   <java-symbol type="id" name="old_app_action" />
   <java-symbol type="id" name="old_app_description" />
@@ -127,7 +124,9 @@
   <java-symbol type="id" name="package_label" />
   <java-symbol type="id" name="packages_list" />
   <java-symbol type="id" name="pause" />
+  <java-symbol type="id" name="perms_list" />
   <java-symbol type="id" name="perm_icon" />
+  <java-symbol type="id" name="perm_name" />
   <java-symbol type="id" name="permission_group" />
   <java-symbol type="id" name="permission_list" />
   <java-symbol type="id" name="pickers" />
@@ -163,9 +162,6 @@
   <java-symbol type="id" name="sha256_fingerprint" />
   <java-symbol type="id" name="share" />
   <java-symbol type="id" name="shortcut" />
-  <java-symbol type="id" name="show_more" />
-  <java-symbol type="id" name="show_more_icon" />
-  <java-symbol type="id" name="show_more_text" />
   <java-symbol type="id" name="skip_button" />
   <java-symbol type="id" name="slider_group" />
   <java-symbol type="id" name="split_action_bar" />
@@ -1044,6 +1040,7 @@
   <java-symbol type="layout" name="alert_dialog_progress" />
   <java-symbol type="layout" name="always_use_checkbox" />
   <java-symbol type="layout" name="app_permission_item" />
+  <java-symbol type="layout" name="app_permission_item_old" />
   <java-symbol type="layout" name="app_perms_summary" />
   <java-symbol type="layout" name="calendar_view" />
   <java-symbol type="layout" name="character_picker" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d779f72..3250507 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2981,7 +2981,7 @@
     <!-- 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>
+    <string name="perms_new_perm_prefix"><font size="12" fgcolor="#ff900000">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.  -->