Merge "Core accessibility settings should not be cleared on restore." into jb-mr1-dev
diff --git a/api/current.txt b/api/current.txt
index db378c8..a1c2902 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -755,6 +755,7 @@
     field public static final int pathPrefix = 16842795; // 0x101002b
     field public static final int permission = 16842758; // 0x1010006
     field public static final int permissionGroup = 16842762; // 0x101000a
+    field public static final int permissionGroupFlags = 16843714; // 0x10103c2
     field public static final int persistent = 16842765; // 0x101000d
     field public static final int persistentDrawingCache = 16842990; // 0x10100ee
     field public static final deprecated int phoneNumber = 16843111; // 0x1010167
@@ -6704,8 +6705,11 @@
     method public int describeContents();
     method public java.lang.CharSequence loadDescription(android.content.pm.PackageManager);
     field public static final android.os.Parcelable.Creator CREATOR;
+    field public static final int FLAG_PERSONAL_INFO = 1; // 0x1
     field public int descriptionRes;
+    field public int flags;
     field public java.lang.CharSequence nonLocalizedDescription;
+    field public int priority;
   }
 
   public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index f874d56..f7460c4 100755
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -958,13 +958,7 @@
         } else if (!mInitialized) {
             initAnimation();
         }
-        // The final value set on the target varies, depending on whether the animation
-        // was supposed to repeat an odd number of times
-        if (mRepeatCount > 0 && (mRepeatCount & 0x01) == 1) {
-            animateValue(0f);
-        } else {
-            animateValue(1f);
-        }
+        animateValue(mPlayingBackwards ? 0f : 1f);
         endAnimation(handler);
     }
 
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 237f5c5..5ebca9e 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1487,7 +1487,8 @@
         perm.info.descriptionRes = sa.getResourceId(
                 com.android.internal.R.styleable.AndroidManifestPermissionGroup_description,
                 0);
-        perm.info.flags = 0;
+        perm.info.flags = sa.getInt(
+                com.android.internal.R.styleable.AndroidManifestPermissionGroup_permissionGroupFlags, 0);
         perm.info.priority = sa.getInt(
                 com.android.internal.R.styleable.AndroidManifestPermissionGroup_priority, 0);
         if (perm.info.priority > 0 && (flags&PARSE_IS_SYSTEM) == 0) {
diff --git a/core/java/android/content/pm/PermissionGroupInfo.java b/core/java/android/content/pm/PermissionGroupInfo.java
index 96d30d4..452bf0d 100644
--- a/core/java/android/content/pm/PermissionGroupInfo.java
+++ b/core/java/android/content/pm/PermissionGroupInfo.java
@@ -44,20 +44,17 @@
     /**
      * Flag for {@link #flags}, corresponding to <code>personalInfo</code>
      * value of {@link android.R.attr#permissionGroupFlags}.
-     * @hide
      */
     public static final int FLAG_PERSONAL_INFO = 1<<0;
 
     /**
      * Additional flags about this group as given by
      * {@link android.R.attr#permissionGroupFlags}.
-     * @hide
      */
     public int flags;
 
     /**
      * Prioritization of this group, for visually sorting with other groups.
-     * @hide
      */
     public int priority;
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1739205..9ea523d 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2616,6 +2616,12 @@
             MOVED_TO_GLOBAL.add(Settings.Global.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED);
             MOVED_TO_GLOBAL.add(Settings.Global.WIFI_WATCHDOG_RSSI_FETCH_INTERVAL_MS);
             MOVED_TO_GLOBAL.add(Settings.Global.WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON);
+            MOVED_TO_GLOBAL.add(Settings.Global.PACKAGE_VERIFIER_ENABLE);
+            MOVED_TO_GLOBAL.add(Settings.Global.PACKAGE_VERIFIER_TIMEOUT);
+            MOVED_TO_GLOBAL.add(Settings.Global.PACKAGE_VERIFIER_DEFAULT_RESPONSE);
+            MOVED_TO_GLOBAL.add(Settings.Global.DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS);
+            MOVED_TO_GLOBAL.add(Settings.Global.DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS);
+            MOVED_TO_GLOBAL.add(Settings.Global.GPRS_REGISTER_CHECK_PERIOD_MS);
             MOVED_TO_GLOBAL.add(Settings.Global.WTF_IS_FATAL);
         }
 
@@ -4254,30 +4260,28 @@
                 Global.PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT;
 
         /**
-         * The number of milliseconds to delay when checking for data stalls during
-         * non-aggressive detection. (screen is turned off.)
+         * @deprecated Moved to Settings.Global
          * @hide
          */
+        @Deprecated
         public static final String DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS =
-                "data_stall_alarm_non_aggressive_delay_in_ms";
+                Global.DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS;
 
         /**
-         * The number of milliseconds to delay when checking for data stalls during
-         * aggressive detection. (screen on or suspected data stall)
+         * @deprecated Moved to Settings.Global
          * @hide
          */
+        @Deprecated
         public static final String DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS =
-                "data_stall_alarm_aggressive_delay_in_ms";
+                Global.DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS;
 
         /**
-         * The interval in milliseconds at which to check gprs registration
-         * after the first registration mismatch of gprs and voice service,
-         * to detect possible data network registration problems.
-         *
+         * @deprecated Moved to Settings.Global
          * @hide
          */
+        @Deprecated
         public static final String GPRS_REGISTER_CHECK_PERIOD_MS =
-                "gprs_register_check_period_ms";
+                Global.GPRS_REGISTER_CHECK_PERIOD_MS;
 
         /**
          * @deprecated Use {@link android.provider.Settings.Global#NITZ_UPDATE_SPACING} instead
@@ -5604,6 +5608,32 @@
        public static final String WIFI_P2P_DEVICE_NAME = "wifi_p2p_device_name";
 
        /**
+        * The number of milliseconds to delay when checking for data stalls during
+        * non-aggressive detection. (screen is turned off.)
+        * @hide
+        */
+       public static final String DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS =
+               "data_stall_alarm_non_aggressive_delay_in_ms";
+
+       /**
+        * The number of milliseconds to delay when checking for data stalls during
+        * aggressive detection. (screen on or suspected data stall)
+        * @hide
+        */
+       public static final String DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS =
+               "data_stall_alarm_aggressive_delay_in_ms";
+
+       /**
+        * The interval in milliseconds at which to check gprs registration
+        * after the first registration mismatch of gprs and voice service,
+        * to detect possible data network registration problems.
+        *
+        * @hide
+        */
+       public static final String GPRS_REGISTER_CHECK_PERIOD_MS =
+               "gprs_register_check_period_ms";
+
+       /**
         * Nonzero causes Log.wtf() to crash.
         * @hide
         */
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 395a2cb..ae10fbe 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -5726,7 +5726,7 @@
         /**
          * Sets the relative start margin.
          *
-         * @param start the start marging size
+         * @param start the start margin size
          *
          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
          */
@@ -5755,7 +5755,7 @@
         /**
          * Sets the relative end margin.
          *
-         * @param end the end marging size
+         * @param end the end margin size
          *
          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
          */
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3006b5d..e1312c1 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3612,8 +3612,31 @@
                 finishInputEvent(q, true);
             } else {
                 if (q.mEvent instanceof KeyEvent) {
+                    KeyEvent event = (KeyEvent)q.mEvent;
+                    if (event.getAction() != KeyEvent.ACTION_UP) {
+                        // If the window doesn't currently have input focus, then drop
+                        // this event.  This could be an event that came back from the
+                        // IME dispatch but the window has lost focus in the meantime.
+                        if (!mAttachInfo.mHasWindowFocus) {
+                            Slog.w(TAG, "Dropping event due to no window focus: " + event);
+                            finishInputEvent(q, true);
+                            return;
+                        }
+                    }
                     deliverKeyEventPostIme(q);
                 } else {
+                    MotionEvent event = (MotionEvent)q.mEvent;
+                    if (event.getAction() != MotionEvent.ACTION_CANCEL
+                            && event.getAction() != MotionEvent.ACTION_UP) {
+                        // If the window doesn't currently have input focus, then drop
+                        // this event.  This could be an event that came back from the
+                        // IME dispatch but the window has lost focus in the meantime.
+                        if (!mAttachInfo.mHasWindowFocus) {
+                            Slog.w(TAG, "Dropping event due to no window focus: " + event);
+                            finishInputEvent(q, true);
+                            return;
+                        }
+                    }
                     final int source = q.mEvent.getSource();
                     if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
                         deliverTrackballEventPostIme(q);
diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java
index 988760d..76b34ef 100755
--- a/core/java/android/widget/AppSecurityPermissions.java
+++ b/core/java/android/widget/AppSecurityPermissions.java
@@ -18,17 +18,25 @@
 
 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;
 import android.content.pm.PackageParser;
+import android.content.pm.PackageUserState;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
 import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
+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;
@@ -36,7 +44,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;
@@ -52,52 +59,200 @@
  * 
  * {@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 LinearLayout mPermsView;
-    private Map<String, String> mDangerousMap;
-    private Map<String, String> mNormalMap;
-    private List<PermissionInfo> mPermsList;
-    private String mDefaultGrpLabel;
-    private String mDefaultGrpName="DefaultGrp";
-    private String mPermFormat;
+    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;
-    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 HashMap<String, CharSequence> mGroupLabelCache;
-    private View mNoPermsView;
-    
+
+    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);
+        }
+
+        public Drawable loadGroupIcon(PackageManager pm) {
+            if (icon != 0) {
+                return loadIcon(pm);
+            } else {
+                ApplicationInfo appInfo;
+                try {
+                    appInfo = pm.getApplicationInfo(packageName, 0);
+                    return appInfo.loadIcon(pm);
+                } catch (NameNotFoundException e) {
+                }
+            }
+            return null;
+        }
+    }
+
+    static class MyPermissionInfo extends PermissionInfo {
+        CharSequence mLabel;
+
+        /**
+         * 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;
+        }
+    }
+
+    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) {
+                icon = grp.loadGroupIcon(pm);
+            }
+            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();
+                }
+                PackageManager pm = getContext().getPackageManager();
+                AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
+                builder.setTitle(mGroup.mLabel);
+                if (mPerm.descriptionRes != 0) {
+                    builder.setMessage(mPerm.loadDescription(pm));
+                } else {
+                    CharSequence appName;
+                    try {
+                        ApplicationInfo app = pm.getApplicationInfo(mPerm.packageName, 0);
+                        appName = app.loadLabel(pm);
+                    } catch (NameNotFoundException e) {
+                        appName = mPerm.packageName;
+                    }
+                    StringBuilder sbuilder = new StringBuilder(128);
+                    sbuilder.append(getContext().getString(
+                            R.string.perms_description_app, appName));
+                    sbuilder.append("\n\n");
+                    sbuilder.append(mPerm.name);
+                    builder.setMessage(sbuilder.toString());
+                }
+                builder.setCancelable(true);
+                builder.setIcon(mGroup.loadGroupIcon(pm));
+                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();
-        mPermsList = permList;
+        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();
-        mPermsList = new ArrayList<PermissionInfo>();
-        Set<PermissionInfo> permSet = new HashSet<PermissionInfo>();
+        loadResources();
+        mPermComparator = new PermissionInfoComparator();
+        mPermGroupComparator = new PermissionGroupInfoComparator();
+        mPermsList = new ArrayList<MyPermissionInfo>();
+        Set<MyPermissionInfo> permSet = new HashSet<MyPermissionInfo>();
         PackageInfo pkgInfo;
         try {
             pkgInfo = mPm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
@@ -109,29 +264,40 @@
         if((pkgInfo.applicationInfo != null) && (pkgInfo.applicationInfo.uid != -1)) {
             getAllUsedPermissions(pkgInfo.applicationInfo.uid, permSet);
         }
-        for(PermissionInfo tmpInfo : permSet) {
+        for(MyPermissionInfo tmpInfo : permSet) {
             mPermsList.add(tmpInfo);
         }
+        setPermissions(mPermsList);
     }
-    
+
     public AppSecurityPermissions(Context context, PackageParser.Package pkg) {
         mContext = context;
         mPm = mContext.getPackageManager();
-        mPermsList = new ArrayList<PermissionInfo>();
-        Set<PermissionInfo> permSet = new HashSet<PermissionInfo>();
+        loadResources();
+        mPermComparator = new PermissionInfoComparator();
+        mPermGroupComparator = new PermissionGroupInfoComparator();
+        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,
+                new PackageUserState());
+        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,13 +307,23 @@
             }
         }
         // Retrieve list of permissions
-        for(PermissionInfo tmpInfo : permSet) {
+        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) {
@@ -155,11 +331,15 @@
                 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);
     }
     
-    private void getAllUsedPermissions(int sharedUid, Set<PermissionInfo> permSet) {
+    public PackageInfo getInstalledPackageInfo() {
+        return mInstalledPackageInfo;
+    }
+
+    private void getAllUsedPermissions(int sharedUid, Set<MyPermissionInfo> permSet) {
         String sharedPkgList[] = mPm.getPackagesForUid(sharedUid);
         if(sharedPkgList == null || (sharedPkgList.length == 0)) {
             return;
@@ -170,29 +350,95 @@
     }
     
     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) {
+        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
+            // 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;
+                }
+                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 = newPerm;
+                permSet.add(myPerm);
             } catch (NameNotFoundException e) {
                 Log.i(TAG, "Ignoring unknown permission:"+permName);
             }
@@ -200,131 +446,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);
-        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);
-        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);
+        }
 
-    /**
-     * 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) {
-            return groupDesc;
-        }
-        // 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(boolean dangerous) {
-        Map<String, String> permInfoMap = dangerous ? mDangerousMap : mNormalMap;
-        LinearLayout permListView = dangerous ? mDangerousList : mNonDangerousList;
+    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);
@@ -341,159 +555,107 @@
         return permView;
     }
 
-    private void showPermissions() {
-
-        switch(mCurrentState) {
-        case NO_PERMS:
-            displayNoPermissions();
-            break;
-
-        case DANGEROUS_ONLY:
-            displayPermissions(true);
-            break;
-
-        case NORMAL_ONLY:
-            displayPermissions(false);
-            break;
-
-        case BOTH:
-            displayPermissions(true);
-            if (mExpanded) {
-                displayPermissions(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;
+        // Dangerous and normal permissions are always shown to the user.
+        if (base == PermissionInfo.PROTECTION_DANGEROUS ||
+                base == PermissionInfo.PROTECTION_NORMAL) {
+            return true;
         }
-    }
-    
-    private boolean isDisplayablePermission(PermissionInfo pInfo) {
-        if(pInfo.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS ||
-                pInfo.protectionLevel == PermissionInfo.PROTECTION_NORMAL) {
+        // 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.
+        if ((existingReqFlags&PackageInfo.REQUESTED_PERMISSION_GRANTED) != 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) {
-        if(map == null) {
-            return;
-        }
-        if(retMap == null) {
-           return;
-        }
-        Set<String> grpNames = map.keySet();
-        Iterator<String> grpNamesIter = grpNames.iterator();
-        while(grpNamesIter.hasNext()) {
-            String grpDesc = null;
-            String grpNameKey = grpNamesIter.next();
-            List<PermissionInfo> grpPermsList = map.get(grpNameKey);
-            if(grpPermsList == null) {
-                continue;
-            }
-            for(PermissionInfo permInfo: grpPermsList) {
-                CharSequence permDesc = permInfo.loadLabel(mPm);
-                grpDesc = formatPermissions(grpDesc, permDesc);
-            }
-            // Insert grpDesc into map
-            if(grpDesc != null) {
-                if(localLOGV) Log.i(TAG, "Group:"+grpNameKey+" description:"+grpDesc.toString());
-                retMap.put(grpNameKey, grpDesc.toString());
-            }
-        }
-    }
-    
-    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 void setPermissions(List<PermissionInfo> 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
-        // categorized as dangerous
-        mDangerousMap = new HashMap<String, String>();
-        // Map containing group names and a list of permissions under that group
-        // categorized as normal
-        mNormalMap = new HashMap<String, String>();
-        
-        // 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>>();
-        PermissionInfoComparator permComparator = new PermissionInfoComparator(mPm);
-        
+    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) {
         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;
-                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);
-                if(grpPermsList == null) {
-                    grpPermsList = new ArrayList<PermissionInfo>();
-                    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(dangerousMap, mDangerousMap);
-            aggregateGroupDescs(normalMap, mNormalMap);
         }
 
-        mCurrentState = State.NO_PERMS;
-        if(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()) {
+            if (pgrp.labelRes != 0 || pgrp.nonLocalizedLabel != null) {
+                pgrp.mLabel = pgrp.loadLabel(mPm);
+            } else {
+                ApplicationInfo app;
+                try {
+                    app = mPm.getApplicationInfo(pgrp.packageName, 0);
+                    pgrp.mLabel = app.loadLabel(mPm);
+                } catch (NameNotFoundException e) {
+                    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);
+        if (false) {
+            for (MyPermissionGroupInfo grp : mPermGroupsList) {
+                Log.i("foo", "Group " + grp.name + " personal="
+                        + ((grp.flags&PermissionGroupInfo.FLAG_PERSONAL_INFO) != 0)
+                        + " priority=" + grp.priority);
+            }
+        }
     }
 }
diff --git a/core/res/res/layout/app_permission_item.xml b/core/res/res/layout/app_permission_item.xml
index ce0cd42..7d44d44 100644
--- a/core/res/res/layout/app_permission_item.xml
+++ b/core/res/res/layout/app_permission_item.xml
@@ -19,37 +19,33 @@
   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_alignParentStart="true"
+        android:layout_width="32dp"
+        android:layout_height="32dp"
+        android:layout_marginStart="16dp"
+        android:layout_marginEnd="8dp"
         android:scaleType="fitCenter" />
 
-
-    <TextView
-        android:id="@+id/permission_group"
-        android:textAppearance="?android:attr/textAppearanceMedium"
-        android:textStyle="bold"
-        android:paddingStart="6dip"
-        android:layout_toEndOf="@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:paddingStart="6dip"
-        android:layout_below="@id/permission_group"
-        android:layout_toEndOf="@id/perm_icon"
+        android:textSize="16sp"
+        android:layout_marginStart="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_perms_summary.xml b/core/res/res/layout/app_perms_summary.xml
index 829d15f..b8d93ac 100755
--- a/core/res/res/layout/app_perms_summary.xml
+++ b/core/res/res/layout/app_perms_summary.xml
@@ -26,79 +26,17 @@
         android:id="@+id/no_permissions"
         android:text="@string/no_permissions"
         android:textAppearance="?android:attr/textAppearanceMedium"
-        android:paddingStart="16dip"
-        android:paddingEnd="12dip"
+        android:paddingStart="8dp"
+        android:paddingEnd="8dp"
         android:visibility="gone"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content" />
 
-    <!-- List view containing list of dangerous permissions categorized by groups. -->
+    <!-- Populated with all permissions. -->
     <LinearLayout
-        android:id="@+id/dangerous_perms_list"
+        android:id="@+id/perms_list"
         android:orientation="vertical"
         android:layout_width="match_parent"
-        android:paddingStart="16dip"
-        android:paddingEnd="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:paddingStart="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:paddingStart="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_marginEnd="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:paddingStart="16dip"
-        android:paddingEnd="12dip"
-        android:layout_width="match_parent"
         android:layout_height="wrap_content" />
 
 </LinearLayout>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 3a69937..4fbe002 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -199,7 +199,7 @@
         <flag name="development" value="0x20" />
     </attr>
 
-    <!-- Flags indicating more context for a permission group. @hide -->
+    <!-- Flags indicating more context for a permission group. -->
     <attr name="permissionGroupFlags">
         <!-- Set to indicate that this permission group contains permissions
              protecting access to some information that is considered
@@ -917,6 +917,7 @@
         <attr name="icon" />
         <attr name="logo" />
         <attr name="description" />
+        <attr name="permissionGroupFlags" />
         <attr name="priority" />
     </declare-styleable>
     
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 02bf710..60b7dba 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1977,7 +1977,6 @@
      =============================================================== -->
   <eat-comment />
   <public type="attr" name="parentActivityName" id="0x010103a7" />
-  <public type="attr" name="permissionGroupFlags" id="0x010103a8" />
   <public type="attr" name="isolatedProcess" id="0x010103a9" />
   <public type="attr" name="importantForAccessibility" id="0x010103aa" />
   <public type="attr" name="keyboardLayout" id="0x010103ab" />
@@ -2016,5 +2015,6 @@
   <public type="attr" name="initialKeyguardLayout" />
   <public type="attr" name="widgetFeatures" />
   <public type="attr" name="widgetCategory" />
+  <public type="attr" name="permissionGroupFlags" />
 
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 8d4fad7..a66bfad 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3039,23 +3039,13 @@
     <!-- Name of the button in the date/time picker to accept the date/time change -->
     <string name="date_time_done">Done</string>
 
-    <!-- Security Permissions strings (old)-->
-    <!-- The default permission group for any permissions that have not explicitly set a group. -->
-    <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>
-    <!-- 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.  -->
-    <string name="perms_hide"><b>Hide</b></string>
-    <!-- When installing an application, the less-dangerous permissions are hidden.  This is the text to show those. -->
-    <string name="perms_show_all"><b>Show all</b></string>
-
-    <!-- Security Permissions strings (new)-->
+    <!-- Security Permissions strings-->
     <!-- 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="#ff900000">NEW: </font></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_description_app">Provided by <xliff:g id="app_name">%1$s</xliff:g>.</string>
+    <!-- Shown for an application when it doesn't require any permission grants. -->
+    <string name="no_permissions">No permissions required</string>
 
     <!-- USB storage dialog strings -->
     <!-- This is the title for the activity's window. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 54dbf2e..505f3a4 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -123,12 +123,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="show_more" />
+  <java-symbol type="id" name="perms_list" />
   <java-symbol type="id" name="perm_icon" />
-  <java-symbol type="id" name="show_more_icon" />
-  <java-symbol type="id" name="show_more_text" />
-  <java-symbol type="id" name="dangerous_perms_list" />
-  <java-symbol type="id" name="non_dangerous_perms_list" />
+  <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" />
@@ -673,10 +670,6 @@
   <java-symbol type="string" name="passwordIncorrect" />
   <java-symbol type="string" name="perms_description_app" />
   <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="default_permission_group" />
-  <java-symbol type="string" name="permissions_format" />
   <java-symbol type="string" name="petabyteShort" />
   <java-symbol type="string" name="phoneTypeAssistant" />
   <java-symbol type="string" name="phoneTypeCallback" />
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index 6c204ab..8b5609f 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -69,10 +69,14 @@
      *          rectangle.
      */
     public Rect(Rect r) {
-        left = r.left;
-        top = r.top;
-        right = r.right;
-        bottom = r.bottom;
+        if (r == null) {
+            left = top = right = bottom = 0;
+        } else {
+            left = r.left;
+            top = r.top;
+            right = r.right;
+            bottom = r.bottom;
+        }
     }
 
     @Override
diff --git a/graphics/java/android/graphics/RectF.java b/graphics/java/android/graphics/RectF.java
index 108b7f9..53178b0 100644
--- a/graphics/java/android/graphics/RectF.java
+++ b/graphics/java/android/graphics/RectF.java
@@ -66,17 +66,25 @@
      *          rectangle.
      */
     public RectF(RectF r) {
-        left = r.left;
-        top = r.top;
-        right = r.right;
-        bottom = r.bottom;
+        if (r == null) {
+            left = top = right = bottom = 0.0f;
+        } else {
+            left = r.left;
+            top = r.top;
+            right = r.right;
+            bottom = r.bottom;
+        }
     }
     
     public RectF(Rect r) {
-        left = r.left;
-        top = r.top;
-        right = r.right;
-        bottom = r.bottom;
+        if (r == null) {
+            left = top = right = bottom = 0.0f;
+        } else {
+            left = r.left;
+            top = r.top;
+            right = r.right;
+            bottom = r.bottom;
+        }
     }
 
     @Override
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/RedEyeFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/RedEyeFilter.java
index 3450ef1..8618804 100644
--- a/media/mca/filterpacks/java/android/filterpacks/imageproc/RedEyeFilter.java
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/RedEyeFilter.java
@@ -72,9 +72,7 @@
             "void main() {\n" +
             "  vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
             "  vec4 mask = texture2D(tex_sampler_1, v_texcoord);\n" +
-            "  gl_FragColor = vec4(mask.a, mask.a, mask.a, 1.0) * intensity + color * (1.0 - intensity);\n" +
             "  if (mask.a > 0.0) {\n" +
-            "    gl_FragColor.r = 0.0;\n" +
             "    float green_blue = color.g + color.b;\n" +
             "    float red_intensity = color.r / green_blue;\n" +
             "    if (red_intensity > intensity) {\n" +
@@ -105,8 +103,8 @@
                 ShaderProgram shaderProgram = new ShaderProgram(context, mRedEyeShader);
                 shaderProgram.setMaximumTileSize(mTileSize);
                 mProgram = shaderProgram;
+                mProgram.setHostValue("intensity", DEFAULT_RED_INTENSITY);
                 break;
-
             default:
                 throw new RuntimeException("Filter RedEye does not support frames of " +
                     "target " + target + "!");
@@ -180,8 +178,6 @@
     }
 
     private void updateProgramParams() {
-        mProgram.setHostValue("intensity", DEFAULT_RED_INTENSITY);
-
         if ( mCenters.length % 2 == 1) {
             throw new RuntimeException("The size of center array must be even.");
         }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 05673c3..23b4b59 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -67,7 +67,7 @@
     // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
     // is properly propagated through your change.  Not doing so will result in a loss of user
     // settings.
-    private static final int DATABASE_VERSION = 87;
+    private static final int DATABASE_VERSION = 88;
 
     private Context mContext;
     private int mUserHandle;
@@ -1305,6 +1305,23 @@
             upgradeVersion = 87;
         }
 
+        if (upgradeVersion == 87) {
+            db.beginTransaction();
+            try {
+                String[] settingsToMove = {
+                        Settings.Secure.DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS,
+                        Settings.Secure.DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS,
+                        Settings.Secure.GPRS_REGISTER_CHECK_PERIOD_MS
+                };
+                moveSettingsToNewTable(db, TABLE_SECURE, TABLE_GLOBAL, settingsToMove, true);
+
+                db.setTransactionSuccessful();
+            } finally {
+                db.endTransaction();
+            }
+            upgradeVersion = 88;
+        }
+
         // *** Remember to update DATABASE_VERSION above!
 
         if (upgradeVersion != currentVersion) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 523b95e..e1a5b52 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -195,6 +195,12 @@
         sSecureGlobalKeys.add(Settings.Secure.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED);
         sSecureGlobalKeys.add(Settings.Secure.WIFI_WATCHDOG_RSSI_FETCH_INTERVAL_MS);
         sSecureGlobalKeys.add(Settings.Secure.WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON);
+        sSecureGlobalKeys.add(Settings.Secure.PACKAGE_VERIFIER_ENABLE);
+        sSecureGlobalKeys.add(Settings.Secure.PACKAGE_VERIFIER_TIMEOUT);
+        sSecureGlobalKeys.add(Settings.Secure.PACKAGE_VERIFIER_DEFAULT_RESPONSE);
+        sSecureGlobalKeys.add(Settings.Secure.DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS);
+        sSecureGlobalKeys.add(Settings.Secure.DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS);
+        sSecureGlobalKeys.add(Settings.Secure.GPRS_REGISTER_CHECK_PERIOD_MS);
         sSecureGlobalKeys.add(Settings.Secure.WTF_IS_FATAL);
 
         // Keys from the 'system' table now moved to 'global'
diff --git a/packages/SystemUI/res/layout/quick_settings_tile_ime.xml b/packages/SystemUI/res/layout/quick_settings_tile_ime.xml
index 93db6db..528b54f 100644
--- a/packages/SystemUI/res/layout/quick_settings_tile_ime.xml
+++ b/packages/SystemUI/res/layout/quick_settings_tile_ime.xml
@@ -15,9 +15,12 @@
 -->
 <TextView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/ime_textview"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:layout_gravity="center"
     android:gravity="center"
+    android:drawableTop="@drawable/stat_sys_roaming_cdma_0"
     android:text="@string/quick_settings_ime_label"
-    android:singleLine="true"
+    android:textAppearance="@style/TextAppearance.QuickSettings.TileView"
     />
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6aed1aa..25bc656 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -410,7 +410,7 @@
     <!-- QuickSettings: Brightness [CHAR LIMIT=NONE] -->
     <string name="quick_settings_brightness_label">Brightness</string>
     <!-- QuickSettings: IME [CHAR LIMIT=NONE] -->
-    <string name="quick_settings_ime_label">IME</string>
+    <string name="quick_settings_ime_label">Input Method</string>
     <!-- QuickSettings: Location [CHAR LIMIT=NONE] -->
     <string name="quick_settings_location_label">Location in use</string>
     <!-- QuickSettings: Media device [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index bee63ee..91f29a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -360,6 +360,10 @@
         mBar = panelBar;
     }
 
+    public void setImeWindowStatus(boolean visible) {
+        // To be implemented by classes extending PanelView
+    }
+
     public void setup(NetworkController network, BluetoothController bt, BatteryController batt,
             LocationController location) {
         // To be implemented by classes extending PanelView
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 6231d0d..b335b5f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1413,10 +1413,11 @@
         mCommandQueue.setNavigationIconHints(
                 altBack ? (mNavigationIconHints | StatusBarManager.NAVIGATION_HINT_BACK_ALT)
                         : (mNavigationIconHints & ~StatusBarManager.NAVIGATION_HINT_BACK_ALT));
+        mSettingsPanel.setImeWindowStatus(vis > 0);
     }
 
     @Override
-    public void setHardKeyboardStatus(boolean available, boolean enabled) { }
+    public void setHardKeyboardStatus(boolean available, boolean enabled) {}
 
     @Override
     protected void tick(IBinder key, StatusBarNotification n, boolean firstTime) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
index 1c3cb2e..c34e012 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.phone;
 
 import android.app.Dialog;
+import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -32,8 +33,8 @@
 import android.hardware.display.WifiDisplay;
 import android.hardware.display.WifiDisplayStatus;
 import android.net.Uri;
-import android.os.UserHandle;
 import android.provider.ContactsContract;
+import android.provider.Settings;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -103,6 +104,10 @@
         mBar = bar;
     }
 
+    public void setImeWindowStatus(boolean visible) {
+        mModel.onImeWindowStatusChanged(visible);
+    }
+
     void setup(NetworkController networkController, BluetoothController bluetoothController,
             BatteryController batteryController, LocationController locationController) {
         networkController.addNetworkSignalChangedCallback(mModel);
@@ -247,29 +252,31 @@
         });
         parent.addView(wifiTile);
 
-        // RSSI
-        QuickSettingsTileView rssiTile = (QuickSettingsTileView)
-                inflater.inflate(R.layout.quick_settings_tile, parent, false);
-        rssiTile.setContent(R.layout.quick_settings_tile_rssi, inflater);
-        rssiTile.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                Intent intent = new Intent();
-                intent.setComponent(new ComponentName(
-                        "com.android.settings",
-                        "com.android.settings.Settings$DataUsageSummaryActivity"));
-                startSettingsActivity(intent);
-            }
-        });
-        mModel.addRSSITile(rssiTile, new QuickSettingsModel.RefreshCallback() {
-            @Override
-            public void refreshView(QuickSettingsTileView view, State state) {
-                TextView tv = (TextView) view.findViewById(R.id.rssi_textview);
-                tv.setCompoundDrawablesRelativeWithIntrinsicBounds(0, state.iconId, 0, 0);
-                tv.setText(state.label);
-            }
-        });
-        parent.addView(rssiTile);
+        if (mModel.deviceSupportsTelephony()) {
+            // RSSI
+            QuickSettingsTileView rssiTile = (QuickSettingsTileView)
+                    inflater.inflate(R.layout.quick_settings_tile, parent, false);
+            rssiTile.setContent(R.layout.quick_settings_tile_rssi, inflater);
+            rssiTile.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    Intent intent = new Intent();
+                    intent.setComponent(new ComponentName(
+                            "com.android.settings",
+                            "com.android.settings.Settings$DataUsageSummaryActivity"));
+                    startSettingsActivity(intent);
+                }
+            });
+            mModel.addRSSITile(rssiTile, new QuickSettingsModel.RefreshCallback() {
+                @Override
+                public void refreshView(QuickSettingsTileView view, State state) {
+                    TextView tv = (TextView) view.findViewById(R.id.rssi_textview);
+                    tv.setCompoundDrawablesRelativeWithIntrinsicBounds(0, state.iconId, 0, 0);
+                    tv.setText(state.label);
+                }
+            });
+            parent.addView(rssiTile);
+        }
 
         // Battery
         QuickSettingsTileView batteryTile = (QuickSettingsTileView)
@@ -386,6 +393,33 @@
         });
         parent.addView(wifiDisplayTile);
 
+        // IME
+        QuickSettingsTileView imeTile = (QuickSettingsTileView)
+                inflater.inflate(R.layout.quick_settings_tile, parent, false);
+        imeTile.setContent(R.layout.quick_settings_tile_ime, inflater);
+        imeTile.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                try {
+                    mBar.collapseAllPanels(true);
+                    Intent intent = new Intent(Settings.ACTION_SHOW_INPUT_METHOD_PICKER);
+                    PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+                    pendingIntent.send();
+                } catch (Exception e) {}
+            }
+        });
+        mModel.addImeTile(imeTile, new QuickSettingsModel.RefreshCallback() {
+            @Override
+            public void refreshView(QuickSettingsTileView view, State state) {
+                TextView tv = (TextView) view.findViewById(R.id.ime_textview);
+                if (state.label != null) {
+                    tv.setText(state.label);
+                }
+                view.setVisibility(state.enabled ? View.VISIBLE : View.GONE);
+            }
+        });
+        parent.addView(imeTile);
+
         /*
         QuickSettingsTileView mediaTile = (QuickSettingsTileView)
                 inflater.inflate(R.layout.quick_settings_tile, parent, false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
index aa40f3c..a0a5282 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
@@ -23,18 +23,25 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.hardware.display.WifiDisplayStatus;
 import android.os.Handler;
 import android.provider.Settings;
+import android.text.TextUtils;
 import android.view.View;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
 import com.android.systemui.statusbar.policy.LocationController.LocationGpsStateChangeCallback;
 import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback;
 
+import java.util.List;
+
 
 class QuickSettingsModel implements BluetoothStateChangeCallback,
         NetworkSignalChangedCallback,
@@ -126,6 +133,10 @@
     private RefreshCallback mLocationCallback;
     private State mLocationState = new State();
 
+    private QuickSettingsTileView mImeTile;
+    private RefreshCallback mImeCallback;
+    private State mImeState = new State();
+
     public QuickSettingsModel(Context context) {
         mContext = context;
         mHandler = new Handler();
@@ -234,20 +245,26 @@
         mRSSICallback = cb;
         mRSSICallback.refreshView(mRSSITile, mRSSIState);
     }
+    boolean deviceSupportsTelephony() {
+        PackageManager pm = mContext.getPackageManager();
+        return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
+    }
     // NetworkSignalChanged callback
     @Override
     public void onMobileDataSignalChanged(boolean enabled, String description) {
-        // TODO: If view is in awaiting state, disable
-        Resources r = mContext.getResources();
-        // TODO: Check if RSSI is enabled
-        mRSSIState.enabled = enabled;
-        mRSSIState.iconId = (enabled ?
-                R.drawable.ic_qs_rssi_enabled :
-                R.drawable.ic_qs_rssi_normal);
-        mRSSIState.label = (enabled ?
-                description :
-                r.getString(R.string.quick_settings_rssi_emergency_only));
-        mRSSICallback.refreshView(mRSSITile, mRSSIState);
+        if (deviceSupportsTelephony()) {
+            // TODO: If view is in awaiting state, disable
+            Resources r = mContext.getResources();
+            // TODO: Check if RSSI is enabled
+            mRSSIState.enabled = enabled;
+            mRSSIState.iconId = (enabled ?
+                    R.drawable.ic_qs_rssi_enabled :
+                    R.drawable.ic_qs_rssi_normal);
+            mRSSIState.label = (enabled ?
+                    description :
+                    r.getString(R.string.quick_settings_rssi_emergency_only));
+            mRSSICallback.refreshView(mRSSITile, mRSSIState);
+        }
     }
 
     // Bluetooth
@@ -320,4 +337,39 @@
 
     }
 
+    // IME
+    void addImeTile(QuickSettingsTileView view, RefreshCallback cb) {
+        mImeTile = view;
+        mImeCallback = cb;
+        mImeCallback.refreshView(mImeTile, mImeState);
+    }
+    void onImeWindowStatusChanged(boolean visible) {
+        InputMethodManager imm =
+                (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
+        List<InputMethodInfo> imis = imm.getInputMethodList();
+
+        mImeState.enabled = visible;
+        mImeState.label = getCurrentInputMethodName(mContext, mContext.getContentResolver(),
+                imm, imis, mContext.getPackageManager());
+        mImeCallback.refreshView(mImeTile, mImeState);
+    }
+    private static String getCurrentInputMethodName(Context context, ContentResolver resolver,
+            InputMethodManager imm, List<InputMethodInfo> imis, PackageManager pm) {
+        if (resolver == null || imis == null) return null;
+        final String currentInputMethodId = Settings.Secure.getString(resolver,
+                Settings.Secure.DEFAULT_INPUT_METHOD);
+        if (TextUtils.isEmpty(currentInputMethodId)) return null;
+        for (InputMethodInfo imi : imis) {
+            if (currentInputMethodId.equals(imi.getId())) {
+                final InputMethodSubtype subtype = imm.getCurrentInputMethodSubtype();
+                final CharSequence summary = subtype != null
+                        ? subtype.getDisplayName(context, imi.getPackageName(),
+                                imi.getServiceInfo().applicationInfo)
+                        : context.getString(R.string.quick_settings_ime_label);
+                return summary.toString();
+            }
+        }
+        return null;
+    }
+
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsPanelView.java
index f896d57..4a7a424 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsPanelView.java
@@ -59,6 +59,13 @@
     }
 
     @Override
+    public void setImeWindowStatus(boolean visible) {
+        if (mQS != null) {
+            mQS.setImeWindowStatus(visible);
+        }
+    }
+
+    @Override
     public void setup(NetworkController networkController, BluetoothController bluetoothController,
             BatteryController batteryController, LocationController locationController) {
         super.setup(networkController, bluetoothController, batteryController, locationController);
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 37dae35..c26448f 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -210,7 +210,7 @@
 
         mBlacklist = new LocationBlacklist(mContext, mLocationHandler);
         mBlacklist.init();
-        mLocationFudger = new LocationFudger();
+        mLocationFudger = new LocationFudger(mContext, mLocationHandler);
 
         synchronized (mLock) {
             loadProvidersLocked();
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 4398441..90783b7 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -55,6 +55,7 @@
 import com.android.server.input.InputManagerService;
 import com.android.server.net.NetworkPolicyManagerService;
 import com.android.server.net.NetworkStatsService;
+import com.android.server.pm.Installer;
 import com.android.server.pm.PackageManagerService;
 import com.android.server.pm.UserManagerService;
 import com.android.server.power.PowerManagerService;
@@ -117,6 +118,7 @@
                 : Integer.parseInt(factoryTestStr);
         final boolean headless = "1".equals(SystemProperties.get("ro.config.headless", "0"));
 
+        Installer installer = null;
         AccountManagerService accountManager = null;
         ContentService contentService = null;
         LightsService lights = null;
@@ -195,6 +197,13 @@
         // Critical services...
         boolean onlyCore = false;
         try {
+            // Wait for installd to finished starting up so that it has a chance to
+            // create critical directories such as /data/user with the appropriate
+            // permissions.  We need this to complete before we initialize other services.
+            Slog.i(TAG, "Waiting for installd to be ready.");
+            installer = new Installer();
+            installer.ping();
+
             Slog.i(TAG, "Entropy Mixer");
             ServiceManager.addService("entropy", new EntropyMixer());
 
@@ -234,7 +243,7 @@
                 onlyCore = true;
             }
 
-            pm = PackageManagerService.main(context,
+            pm = PackageManagerService.main(context, installer,
                     factoryTest != SystemServer.FACTORY_TEST_OFF,
                     onlyCore);
             boolean firstBoot = false;
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index f6354bb..99ec1d2 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -682,11 +682,7 @@
         for (int i = 0, count = installedServices.size(); i < count; i++) {
             ResolveInfo resolveInfo = installedServices.get(i);
             ServiceInfo serviceInfo = resolveInfo.serviceInfo;
-            // For now we are enforcing this if the target version is JellyBean or
-            // higher and in a later release we will enforce this for everyone.
-            if (serviceInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN
-                    && !android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE.equals(
-                    serviceInfo.permission)) {
+            if (!android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE.equals(serviceInfo.permission)) {
                 Slog.w(LOG_TAG, "Skipping accessibilty service " + new ComponentName(
                         serviceInfo.packageName, serviceInfo.name).flattenToShortString()
                         + ": it does not require the permission "
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index 3bb95a8..aefc264 100644
--- a/services/java/com/android/server/am/ActiveServices.java
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -478,12 +478,6 @@
         if (res.record == null) {
             return -1;
         }
-        if (mAm.isSingleton(res.record.processName, res.record.appInfo,
-                res.record.serviceInfo.name, res.record.serviceInfo.flags)) {
-            userId = 0;
-            res = retrieveServiceLocked(service, resolvedType, Binder.getCallingPid(),
-                    Binder.getCallingUid(), 0, true);
-        }
         ServiceRecord s = res.record;
 
         final long origId = Binder.clearCallingIdentity();
diff --git a/services/java/com/android/server/location/LocationFudger.java b/services/java/com/android/server/location/LocationFudger.java
index 57bc1c5..84fd255 100644
--- a/services/java/com/android/server/location/LocationFudger.java
+++ b/services/java/com/android/server/location/LocationFudger.java
@@ -19,10 +19,14 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.security.SecureRandom;
+import android.content.Context;
+import android.database.ContentObserver;
 import android.location.Location;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.Parcelable;
 import android.os.SystemClock;
+import android.provider.Settings;
 import android.util.Log;
 
 
@@ -39,22 +43,19 @@
     private static final String EXTRA_COARSE_LOCATION = "coarseLocation";
 
     /**
-     * This is the main control: Best location accuracy allowed for coarse applications.
+     * Default coarse accuracy in meters.
      */
-    private static final float ACCURACY_METERS = 200.0f;
+    private static final float DEFAULT_ACCURACY_IN_METERS = 2000.0f;
 
     /**
-     * The distance between grids for snap-to-grid. See {@link #createCoarse}.
+     * Minimum coarse accuracy in meters.
      */
-    private static final double GRID_SIZE_METERS = ACCURACY_METERS;
+    private static final float MINIMUM_ACCURACY_IN_METERS = 200.0f;
 
     /**
-     * Standard deviation of the (normally distributed) random offset applied
-     * to coarse locations. It does not need to be as large as
-     * {@link #COARSE_ACCURACY_METERS} because snap-to-grid is the primary obfuscation
-     * method. See further details in the implementation.
+     * Secure settings key for coarse accuracy.
      */
-    private static final double STANDARD_DEVIATION_METERS = GRID_SIZE_METERS / 4.0;
+    private static final String COARSE_ACCURACY_CONFIG_NAME = "locationCoarseAccuracy";
 
     /**
      * This is the fastest interval that applications can receive coarse
@@ -106,43 +107,90 @@
     private final Object mLock = new Object();
     private final SecureRandom mRandom = new SecureRandom();
 
+    /**
+     * Used to monitor coarse accuracy secure setting for changes.
+     */
+    private final ContentObserver mSettingsObserver;
+
+    /**
+     * Used to resolve coarse accuracy setting.
+     */
+    private final Context mContext;
+
     // all fields below protected by mLock
     private double mOffsetLatitudeMeters;
     private double mOffsetLongitudeMeters;
     private long mNextInterval;
 
-    public LocationFudger() {
-        mOffsetLatitudeMeters = nextOffset();
-        mOffsetLongitudeMeters = nextOffset();
-        mNextInterval = SystemClock.elapsedRealtime() + CHANGE_INTERVAL_MS;
+    /**
+     * Best location accuracy allowed for coarse applications.
+     * This value should only be set by {@link #setAccuracyInMetersLocked(float)}.
+     */
+    private float mAccuracyInMeters;
+
+    /**
+     * The distance between grids for snap-to-grid. See {@link #createCoarse}.
+     * This value should only be set by {@link #setAccuracyInMetersLocked(float)}.
+     */
+    private double mGridSizeInMeters;
+
+    /**
+     * Standard deviation of the (normally distributed) random offset applied
+     * to coarse locations. It does not need to be as large as
+     * {@link #COARSE_ACCURACY_METERS} because snap-to-grid is the primary obfuscation
+     * method. See further details in the implementation.
+     * This value should only be set by {@link #setAccuracyInMetersLocked(float)}.
+     */
+    private double mStandardDeviationInMeters;
+
+    public LocationFudger(Context context, Handler handler) {
+        mContext = context;
+        mSettingsObserver = new ContentObserver(handler) {
+            @Override
+            public void onChange(boolean selfChange) {
+                setAccuracyInMeters(loadCoarseAccuracy());
+            }
+        };
+        mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
+                COARSE_ACCURACY_CONFIG_NAME), false, mSettingsObserver);
+
+        float accuracy = loadCoarseAccuracy();
+        synchronized (mLock) {
+            setAccuracyInMetersLocked(accuracy);
+            mOffsetLatitudeMeters = nextOffsetLocked();
+            mOffsetLongitudeMeters = nextOffsetLocked();
+            mNextInterval = SystemClock.elapsedRealtime() + CHANGE_INTERVAL_MS;
+        }
     }
 
     /**
      * Get the cached coarse location, or generate a new one and cache it.
      */
     public Location getOrCreate(Location location) {
-        Bundle extras = location.getExtras();
-        if (extras == null) {
-            return addCoarseLocationExtra(location);
+        synchronized (mLock) {
+            Bundle extras = location.getExtras();
+            if (extras == null) {
+                return addCoarseLocationExtraLocked(location);
+            }
+            Parcelable parcel = extras.getParcelable(EXTRA_COARSE_LOCATION);
+            if (parcel == null) {
+                return addCoarseLocationExtraLocked(location);
+            }
+            if (!(parcel instanceof Location)) {
+                return addCoarseLocationExtraLocked(location);
+            }
+            Location coarse = (Location) parcel;
+            if (coarse.getAccuracy() < mAccuracyInMeters) {
+                return addCoarseLocationExtraLocked(location);
+            }
+            return coarse;
         }
-        Parcelable parcel = extras.getParcelable(EXTRA_COARSE_LOCATION);
-        if (parcel == null) {
-            return addCoarseLocationExtra(location);
-        }
-        if (!(parcel instanceof Location)) {
-            return addCoarseLocationExtra(location);
-        }
-        Location coarse = (Location) parcel;
-        if (coarse.getAccuracy() < ACCURACY_METERS) {
-            return addCoarseLocationExtra(location);
-        }
-        return coarse;
     }
 
-    private Location addCoarseLocationExtra(Location location) {
+    private Location addCoarseLocationExtraLocked(Location location) {
         Bundle extras = location.getExtras();
         if (extras == null) extras = new Bundle();
-        Location coarse = createCoarse(location);
+        Location coarse = createCoarseLocked(location);
         extras.putParcelable(EXTRA_COARSE_LOCATION, coarse);
         location.setExtras(extras);
         return coarse;
@@ -163,7 +211,7 @@
      * producing stable results, and mitigating against taking many samples
      * to average out a random offset.
      */
-    private Location createCoarse(Location fine) {
+    private Location createCoarseLocked(Location fine) {
         Location coarse = new Location(fine);
 
         // clean all the optional information off the location, because
@@ -188,14 +236,12 @@
         //
         // We apply the offset even if the location already claims to be
         // inaccurate, because it may be more accurate than claimed.
-        synchronized (mLock) {
-            updateRandomOffsetLocked();
-            // perform lon first whilst lat is still within bounds
-            lon += metersToDegreesLongitude(mOffsetLongitudeMeters, lat);
-            lat += metersToDegreesLatitude(mOffsetLatitudeMeters);
-            if (D) Log.d(TAG, String.format("applied offset of %.0f, %.0f (meters)",
-                    mOffsetLongitudeMeters, mOffsetLatitudeMeters));
-        }
+        updateRandomOffsetLocked();
+        // perform lon first whilst lat is still within bounds
+        lon += metersToDegreesLongitude(mOffsetLongitudeMeters, lat);
+        lat += metersToDegreesLatitude(mOffsetLatitudeMeters);
+        if (D) Log.d(TAG, String.format("applied offset of %.0f, %.0f (meters)",
+                mOffsetLongitudeMeters, mOffsetLatitudeMeters));
 
         // wrap
         lat = wrapLatitude(lat);
@@ -211,9 +257,9 @@
         // Note we quantize the latitude first, since the longitude
         // quantization depends on the latitude value and so leaks information
         // about the latitude
-        double latGranularity = metersToDegreesLatitude(GRID_SIZE_METERS);
+        double latGranularity = metersToDegreesLatitude(mGridSizeInMeters);
         lat = Math.round(lat / latGranularity) * latGranularity;
-        double lonGranularity = metersToDegreesLongitude(GRID_SIZE_METERS, lat);
+        double lonGranularity = metersToDegreesLongitude(mGridSizeInMeters, lat);
         lon = Math.round(lon / lonGranularity) * lonGranularity;
 
         // wrap again
@@ -223,7 +269,7 @@
         // apply
         coarse.setLatitude(lat);
         coarse.setLongitude(lon);
-        coarse.setAccuracy(Math.max(ACCURACY_METERS, coarse.getAccuracy()));
+        coarse.setAccuracy(Math.max(mAccuracyInMeters, coarse.getAccuracy()));
 
         if (D) Log.d(TAG, "fudged " + fine + " to " + coarse);
         return coarse;
@@ -259,16 +305,16 @@
         mNextInterval = now + CHANGE_INTERVAL_MS;
 
         mOffsetLatitudeMeters *= PREVIOUS_WEIGHT;
-        mOffsetLatitudeMeters += NEW_WEIGHT * nextOffset();
+        mOffsetLatitudeMeters += NEW_WEIGHT * nextOffsetLocked();
         mOffsetLongitudeMeters *= PREVIOUS_WEIGHT;
-        mOffsetLongitudeMeters += NEW_WEIGHT * nextOffset();
+        mOffsetLongitudeMeters += NEW_WEIGHT * nextOffsetLocked();
 
         if (D) Log.d(TAG, String.format("new offset: %.0f, %.0f (meters)",
                 mOffsetLongitudeMeters, mOffsetLatitudeMeters));
     }
 
-    private double nextOffset() {
-        return mRandom.nextGaussian() * STANDARD_DEVIATION_METERS;
+    private double nextOffsetLocked() {
+        return mRandom.nextGaussian() * mStandardDeviationInMeters;
     }
 
     private static double wrapLatitude(double lat) {
@@ -307,4 +353,45 @@
         pw.println(String.format("offset: %.0f, %.0f (meters)", mOffsetLongitudeMeters,
                 mOffsetLatitudeMeters));
     }
+
+    /**
+     * This is the main control: call this to set the best location accuracy
+     * allowed for coarse applications and all derived values.
+     */
+    private void setAccuracyInMetersLocked(float accuracyInMeters) {
+        mAccuracyInMeters = Math.max(accuracyInMeters, MINIMUM_ACCURACY_IN_METERS);
+        if (D) {
+            Log.d(TAG, "setAccuracyInMetersLocked: new accuracy = " + mAccuracyInMeters);
+        }
+        mGridSizeInMeters = mAccuracyInMeters;
+        mStandardDeviationInMeters = mGridSizeInMeters / 4.0;
+    }
+
+    /**
+     * Same as setAccuracyInMetersLocked without the pre-lock requirement.
+     */
+    private void setAccuracyInMeters(float accuracyInMeters) {
+        synchronized (mLock) {
+            setAccuracyInMetersLocked(accuracyInMeters);
+        }
+    }
+
+    /**
+     * Loads the coarse accuracy value from secure settings.
+     */
+    private float loadCoarseAccuracy() {
+        String newSetting = Settings.Secure.getString(mContext.getContentResolver(),
+                COARSE_ACCURACY_CONFIG_NAME);
+        if (D) {
+            Log.d(TAG, "loadCoarseAccuracy: newSetting = \"" + newSetting + "\"");
+        }
+        if (newSetting == null) {
+            return DEFAULT_ACCURACY_IN_METERS;
+        }
+        try {
+            return Float.parseFloat(newSetting);
+        } catch (NumberFormatException e) {
+            return DEFAULT_ACCURACY_IN_METERS;
+        }
+    }
 }
diff --git a/services/java/com/android/server/pm/Installer.java b/services/java/com/android/server/pm/Installer.java
index 85de349..ad85c0d 100644
--- a/services/java/com/android/server/pm/Installer.java
+++ b/services/java/com/android/server/pm/Installer.java
@@ -25,7 +25,7 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 
-class Installer {
+public final class Installer {
     private static final String TAG = "Installer";
 
     private static final boolean LOCAL_DEBUG = false;
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 01a9b4b..ba63efd 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -937,9 +937,10 @@
         }
     }
 
-    public static final IPackageManager main(Context context, boolean factoryTest,
-            boolean onlyCore) {
-        PackageManagerService m = new PackageManagerService(context, factoryTest, onlyCore);
+    public static final IPackageManager main(Context context, Installer installer,
+            boolean factoryTest, boolean onlyCore) {
+        PackageManagerService m = new PackageManagerService(context, installer,
+                factoryTest, onlyCore);
         ServiceManager.addService("package", m);
         return m;
     }
@@ -966,7 +967,8 @@
         return res;
     }
 
-    public PackageManagerService(Context context, boolean factoryTest, boolean onlyCore) {
+    public PackageManagerService(Context context, Installer installer,
+            boolean factoryTest, boolean onlyCore) {
         EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
                 SystemClock.uptimeMillis());
 
@@ -1004,7 +1006,7 @@
             mSeparateProcesses = null;
         }
 
-        mInstaller = new Installer();
+        mInstaller = installer;
 
         WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
         Display d = wm.getDefaultDisplay();