Merge changes I51f2e466,I5b67cb3e into lmp-dev

* changes:
  Make PlaybackState immutable with a builder
  Add API to set a default session in Activity
diff --git a/api/current.txt b/api/current.txt
index 18d8e6f..77cc1f6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8369,6 +8369,9 @@
     field public java.lang.String targetPackage;
   }
 
+  public class KeySet {
+  }
+
   public class LabeledIntent extends android.content.Intent {
     ctor public LabeledIntent(android.content.Intent, java.lang.String, int, int);
     ctor public LabeledIntent(android.content.Intent, java.lang.String, java.lang.CharSequence, int);
@@ -8506,6 +8509,7 @@
     method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public abstract java.lang.String getInstallerPackageName(java.lang.String);
     method public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public abstract android.content.pm.KeySet getKeySetByAlias(java.lang.String, java.lang.String);
     method public abstract android.content.Intent getLaunchIntentForPackage(java.lang.String);
     method public abstract android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
     method public abstract java.lang.String getNameForUid(int);
@@ -8524,12 +8528,15 @@
     method public abstract android.content.res.Resources getResourcesForApplication(android.content.pm.ApplicationInfo) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.res.Resources getResourcesForApplication(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.pm.ServiceInfo getServiceInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public abstract android.content.pm.KeySet getSigningKeySet(java.lang.String);
     method public abstract android.content.pm.FeatureInfo[] getSystemAvailableFeatures();
     method public abstract java.lang.String[] getSystemSharedLibraryNames();
     method public abstract java.lang.CharSequence getText(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public abstract boolean hasSystemFeature(java.lang.String);
     method public abstract boolean isSafeMode();
+    method public abstract boolean isSignedBy(java.lang.String, android.content.pm.KeySet);
+    method public abstract boolean isSignedByExactly(java.lang.String, android.content.pm.KeySet);
     method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
     method public abstract java.util.List<android.content.pm.ProviderInfo> queryContentProviders(java.lang.String, int, int);
     method public abstract java.util.List<android.content.pm.InstrumentationInfo> queryInstrumentation(java.lang.String, int);
@@ -22944,7 +22951,7 @@
     method protected void onDisconnected();
     method protected abstract void onPrintJobQueued(android.printservice.PrintJob);
     method protected abstract void onRequestCancelPrintJob(android.printservice.PrintJob);
-    field public static final java.lang.String EXTRA_PRINTER_INFO = "android.intent.extra.print.PRINTER_INFO";
+    field public static final java.lang.String EXTRA_PRINTER_INFO = "android.intent.extra.print.EXTRA_PRINTER_INFO";
     field public static final java.lang.String EXTRA_PRINT_JOB_INFO = "android.intent.extra.print.PRINT_JOB_INFO";
     field public static final java.lang.String SERVICE_INTERFACE = "android.printservice.PrintService";
     field public static final java.lang.String SERVICE_META_DATA = "android.printservice";
@@ -25589,6 +25596,7 @@
     field public static final java.lang.String NUMBER = "number";
     field public static final java.lang.String SOURCE_DATA = "source_data";
     field public static final java.lang.String SOURCE_PACKAGE = "source_package";
+    field public static final java.lang.String TRANSCRIPTION = "transcription";
   }
 
 }
@@ -28223,6 +28231,7 @@
     ctor public TelecommConstants();
     field public static final java.lang.String ACTION_CALL_SERVICE_PROVIDER;
     field public static final java.lang.String ACTION_CONNECTION_SERVICE;
+    field public static final java.lang.String ACTION_CONNECTION_SERVICE_CONFIGURE = "android.intent.action.CONNECTION_SERVICE_CONFIGURE";
     field public static final java.lang.String ACTION_INCOMING_CALL = "android.intent.action.INCOMING_CALL";
     field public static final char DTMF_CHARACTER_PAUSE = 44; // 0x002c ','
     field public static final char DTMF_CHARACTER_WAIT = 59; // 0x003b ';'
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 3d0eec4..3a2ca30 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -50,6 +50,7 @@
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.content.PackageHelper;
@@ -923,33 +924,13 @@
                     return;
                 }
             } else if (opt.equals("--abi")) {
-                abi = nextOptionData();
-                if (abi == null) {
-                    System.err.println("Error: must supply argument for --abi");
-                    return;
-                }
+                abi = checkAbiArgument(nextOptionData());
             } else {
                 System.err.println("Error: Unknown option: " + opt);
                 return;
             }
         }
 
-        if (abi != null) {
-            final String[] supportedAbis = Build.SUPPORTED_ABIS;
-            boolean matched = false;
-            for (String supportedAbi : supportedAbis) {
-                if (supportedAbi.equals(abi)) {
-                    matched = true;
-                    break;
-                }
-            }
-
-            if (!matched) {
-                System.err.println("Error: abi " + abi + " not supported on this device.");
-                return;
-            }
-        }
-
         final Uri verificationURI;
         final Uri originatingURI;
         final Uri referrerURI;
@@ -1044,6 +1025,8 @@
             } else if (opt.equals("-S")) {
                 params.deltaSize = Long.parseLong(nextOptionData());
                 params.progressMax = (int) params.deltaSize;
+            } else if (opt.equals("--abi")) {
+                params.abiOverride = checkAbiArgument(nextOptionData());
             } else {
                 throw new IllegalArgumentException("Unknown option " + opt);
             }
@@ -1684,6 +1667,21 @@
         }
     }
 
+    private static String checkAbiArgument(String abi) {
+        if (TextUtils.isEmpty(abi)) {
+            throw new IllegalArgumentException("Missing ABI argument");
+        }
+
+        final String[] supportedAbis = Build.SUPPORTED_ABIS;
+        for (String supportedAbi : supportedAbis) {
+            if (supportedAbi.equals(abi)) {
+                return abi;
+            }
+        }
+
+        throw new IllegalArgumentException("ABI " + abi + " not supported on this device");
+    }
+
     private String nextOption() {
         if (mNextArg >= mArgs.length) {
             return null;
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index 15be9b1..e074219 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -403,20 +403,18 @@
         view.layout(left, top, right, bottom);
     }
 
-    protected ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> setSharedElementState(
+    protected ArrayList<SharedElementOriginalState> setSharedElementState(
             Bundle sharedElementState, final ArrayList<View> snapshots) {
-        ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageState =
-                new ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>>();
+        ArrayList<SharedElementOriginalState> originalImageState =
+                new ArrayList<SharedElementOriginalState>();
         if (sharedElementState != null) {
             int[] tempLoc = new int[2];
             for (int i = 0; i < mSharedElementNames.size(); i++) {
                 View sharedElement = mSharedElements.get(i);
                 String name = mSharedElementNames.get(i);
-                Pair<ImageView.ScaleType, Matrix> originalState = getOldImageState(sharedElement,
+                SharedElementOriginalState originalState = getOldSharedElementState(sharedElement,
                         name, sharedElementState);
-                if (originalState != null) {
-                    originalImageState.put((ImageView) sharedElement, originalState);
-                }
+                originalImageState.add(originalState);
                 View parent = (View) sharedElement.getParent();
                 parent.getLocationOnScreen(tempLoc);
                 setSharedElementState(sharedElement, name, sharedElementState, tempLoc);
@@ -438,29 +436,34 @@
         return originalImageState;
     }
 
-    private static Pair<ImageView.ScaleType, Matrix> getOldImageState(View view, String name,
+    private static SharedElementOriginalState getOldSharedElementState(View view, String name,
             Bundle transitionArgs) {
+
+        SharedElementOriginalState state = new SharedElementOriginalState();
+        state.mLeft = view.getLeft();
+        state.mTop = view.getTop();
+        state.mRight = view.getRight();
+        state.mBottom = view.getBottom();
+        state.mMeasuredWidth = view.getMeasuredWidth();
+        state.mMeasuredHeight = view.getMeasuredHeight();
         if (!(view instanceof ImageView)) {
-            return null;
+            return state;
         }
         Bundle bundle = transitionArgs.getBundle(name);
         if (bundle == null) {
-            return null;
+            return state;
         }
         int scaleTypeInt = bundle.getInt(KEY_SCALE_TYPE, -1);
         if (scaleTypeInt < 0) {
-            return null;
+            return state;
         }
 
         ImageView imageView = (ImageView) view;
-        ImageView.ScaleType originalScaleType = imageView.getScaleType();
-
-        Matrix originalMatrix = null;
-        if (originalScaleType == ImageView.ScaleType.MATRIX) {
-            originalMatrix = new Matrix(imageView.getImageMatrix());
+        state.mScaleType = imageView.getScaleType();
+        if (state.mScaleType == ImageView.ScaleType.MATRIX) {
+            state.mMatrix = new Matrix(imageView.getImageMatrix());
         }
-
-        return Pair.create(originalScaleType, originalMatrix);
+        return state;
     }
 
     protected ArrayList<View> createSnapshots(Bundle state, Collection<String> names) {
@@ -489,13 +492,26 @@
         return snapshots;
     }
 
-    protected static void setOriginalImageViewState(
-            ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalState) {
+    protected static void setOriginalSharedElementState(ArrayList<View> sharedElements,
+            ArrayList<SharedElementOriginalState> originalState) {
         for (int i = 0; i < originalState.size(); i++) {
-            ImageView imageView = originalState.keyAt(i);
-            Pair<ImageView.ScaleType, Matrix> state = originalState.valueAt(i);
-            imageView.setScaleType(state.first);
-            imageView.setImageMatrix(state.second);
+            View view = sharedElements.get(i);
+            SharedElementOriginalState state = originalState.get(i);
+            if (view instanceof ImageView && state.mScaleType != null) {
+                ImageView imageView = (ImageView) view;
+                imageView.setScaleType(state.mScaleType);
+                if (state.mScaleType == ImageView.ScaleType.MATRIX) {
+                  imageView.setImageMatrix(state.mMatrix);
+                }
+            }
+            // origignal widthspec might be AT_MOST,  but it should work for most
+            // cases.
+            int widthSpec = View.MeasureSpec.makeMeasureSpec(state.mMeasuredWidth,
+                    View.MeasureSpec.EXACTLY);
+            int heightSpec = View.MeasureSpec.makeMeasureSpec(state.mMeasuredHeight,
+                    View.MeasureSpec.EXACTLY);
+            view.measure(widthSpec, heightSpec);
+            view.layout(state.mLeft, state.mTop, state.mRight, state.mBottom);
         }
     }
 
@@ -622,4 +638,15 @@
         }
     }
 
+    static class SharedElementOriginalState {
+        int mLeft;
+        int mTop;
+        int mRight;
+        int mBottom;
+        int mMeasuredWidth;
+        int mMeasuredHeight;
+        ImageView.ScaleType mScaleType;
+        Matrix mMatrix;
+    }
+
 }
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 2935b8e..4730559 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -33,6 +33,7 @@
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageStatsObserver;
 import android.content.pm.InstrumentationInfo;
+import android.content.pm.KeySet;
 import android.content.pm.ManifestDigest;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
@@ -52,6 +53,7 @@
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.IBinder;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -59,6 +61,7 @@
 import android.util.ArrayMap;
 import android.util.Log;
 import android.view.Display;
+import com.android.internal.util.Preconditions;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -1447,6 +1450,62 @@
         return false;
     }
 
+    @Override
+    public KeySet getKeySetByAlias(String packageName, String alias) {
+        Preconditions.checkNotNull(packageName);
+        Preconditions.checkNotNull(alias);
+        IBinder keySetToken;
+        try {
+            keySetToken = mPM.getKeySetByAlias(packageName, alias);
+        } catch (RemoteException e) {
+            return null;
+        }
+        if (keySetToken == null) {
+            return null;
+        }
+        return new KeySet(keySetToken);
+    }
+
+    @Override
+    public KeySet getSigningKeySet(String packageName) {
+        Preconditions.checkNotNull(packageName);
+        IBinder keySetToken;
+        try {
+            keySetToken = mPM.getSigningKeySet(packageName);
+        } catch (RemoteException e) {
+            return null;
+        }
+        if (keySetToken == null) {
+            return null;
+        }
+        return new KeySet(keySetToken);
+    }
+
+
+    @Override
+    public boolean isSignedBy(String packageName, KeySet ks) {
+        Preconditions.checkNotNull(packageName);
+        Preconditions.checkNotNull(ks);
+        IBinder keySetToken = ks.getToken();
+        try {
+            return mPM.isPackageSignedByKeySet(packageName, keySetToken);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean isSignedByExactly(String packageName, KeySet ks) {
+        Preconditions.checkNotNull(packageName);
+        Preconditions.checkNotNull(ks);
+        IBinder keySetToken = ks.getToken();
+        try {
+            return mPM.isPackageSignedByKeySetExactly(packageName, keySetToken);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
     /**
      * @hide
      */
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index f50c93b..1326064 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -282,7 +282,7 @@
         ArrayList<View> sharedElementSnapshots = createSnapshots(sharedElementState,
                 mSharedElementNames);
         setTransitionAlpha(mSharedElements, 1);
-        ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageViewState =
+        ArrayList<SharedElementOriginalState> originalImageViewState =
                 setSharedElementState(sharedElementState, sharedElementSnapshots);
         requestLayoutForSharedElements();
 
@@ -294,7 +294,7 @@
             startEnterTransition(transition);
         }
 
-        setOriginalImageViewState(originalImageViewState);
+        setOriginalSharedElementState(mSharedElements, originalImageViewState);
 
         if (mResultReceiver != null) {
             // We can't trust that the view will disappear on the same frame that the shared
diff --git a/core/java/android/app/PackageInstallObserver.java b/core/java/android/app/PackageInstallObserver.java
index 7117111..1b2504e 100644
--- a/core/java/android/app/PackageInstallObserver.java
+++ b/core/java/android/app/PackageInstallObserver.java
@@ -25,7 +25,7 @@
         @Override
         public void packageInstalled(String basePackageName, Bundle extras, int returnCode,
                 String msg) {
-            PackageInstallObserver.this.packageInstalled(basePackageName, extras, returnCode);
+            PackageInstallObserver.this.packageInstalled(basePackageName, extras, returnCode, msg);
         }
     };
 
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 58d3526..3a98f5d 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -429,4 +429,9 @@
 
     boolean setBlockUninstallForUser(String packageName, boolean blockUninstall, int userId);
     boolean getBlockUninstallForUser(String packageName, int userId);
+
+    IBinder getKeySetByAlias(String packageName, String alias);
+    IBinder getSigningKeySet(String packageName);
+    boolean isPackageSignedByKeySet(String packageName, IBinder ks);
+    boolean isPackageSignedByKeySetExactly(String packageName, IBinder ks);
 }
diff --git a/core/java/android/content/pm/KeySet.java b/core/java/android/content/pm/KeySet.java
index 0ef09a4..fcdaa18 100644
--- a/core/java/android/content/pm/KeySet.java
+++ b/core/java/android/content/pm/KeySet.java
@@ -16,19 +16,36 @@
 
 package android.content.pm;
 
-import android.os.Binder;
+import android.os.IBinder;
 
-/** @hide */
+/**
+ * Represents a {@code KeySet} that has been declared in the AndroidManifest.xml
+ * file for the application.  A {@code KeySet} can be used explicitly to
+ * represent a trust relationship with other applications on the device.
+ */
 public class KeySet {
 
-    private Binder token;
+    private IBinder token;
 
     /** @hide */
-    public KeySet(Binder token) {
+    public KeySet(IBinder token) {
+        if (token == null) {
+            throw new NullPointerException("null value for KeySet IBinder token");
+        }
         this.token = token;
     }
 
-    Binder getToken() {
+    /** @hide */
+    public IBinder getToken() {
         return token;
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof KeySet) {
+            KeySet ks = (KeySet) o;
+            return token == ks.token;
+        }
+        return false;
+    }
 }
\ No newline at end of file
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 03d4701..91ebbbf 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3602,6 +3602,33 @@
     public abstract boolean isSafeMode();
 
     /**
+     * Return the {@link KeySet} associated with the String alias for this
+     * application.
+     *
+     * @param alias The alias for a given {@link KeySet} as defined in the
+     *        application's AndroidManifest.xml.
+     */
+    public abstract KeySet getKeySetByAlias(String packageName, String alias);
+
+    /** Return the signing {@link KeySet} for this application. */
+    public abstract KeySet getSigningKeySet(String packageName);
+
+    /**
+     * Return whether the package denoted by packageName has been signed by all
+     * of the keys specified by the {@link KeySet} ks.  This will return true if
+     * the package has been signed by additional keys (a superset) as well.
+     * Compare to {@link #isSignedByExactly(String packageName, KeySet ks)}.
+     */
+    public abstract boolean isSignedBy(String packageName, KeySet ks);
+
+    /**
+     * Return whether the package denoted by packageName has been signed by all
+     * of, and only, the keys specified by the {@link KeySet} ks. Compare to
+     * {@link #isSignedBy(String packageName, KeySet ks)}.
+     */
+    public abstract boolean isSignedByExactly(String packageName, KeySet ks);
+
+    /**
      * Attempts to move package resources from internal to external media or vice versa.
      * Since this may take a little while, the result will
      * be posted back to the given observer.   This call may fail if the calling context
diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java
index 1557ab0..c5aee7b 100644
--- a/core/java/android/printservice/PrintService.java
+++ b/core/java/android/printservice/PrintService.java
@@ -230,7 +230,7 @@
      *
      * @see #EXTRA_PRINT_JOB_INFO
      */
-    public static final String EXTRA_PRINTER_INFO = "android.intent.extra.print.PRINTER_INFO";
+    public static final String EXTRA_PRINTER_INFO = "android.intent.extra.print.EXTRA_PRINTER_INFO";
 
     private Handler mHandler;
 
diff --git a/core/java/android/provider/VoicemailContract.java b/core/java/android/provider/VoicemailContract.java
index 6787fd0..d71ad03 100644
--- a/core/java/android/provider/VoicemailContract.java
+++ b/core/java/android/provider/VoicemailContract.java
@@ -188,6 +188,12 @@
          */
         public static final String MIME_TYPE = "mime_type";
         /**
+         * The transcription of the voicemail entry. This will only be populated if the voicemail
+         * entry has a valid transcription.
+         * <P>Type: TEXT</P>
+         */
+        public static final String TRANSCRIPTION = "transcription";
+        /**
          * Path to the media content file. Internal only field.
          * @hide
          */
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 9701c6f..aa0b94f 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -36,7 +36,6 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.LongSparseArray;
-import android.util.MathUtils;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.StateSet;
@@ -61,8 +60,6 @@
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.CollectionInfo;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.view.animation.LinearInterpolator;
 import android.view.inputmethod.BaseInputConnection;
@@ -7269,290 +7266,4 @@
             }
         }
     }
-
-    /**
-     * Abstract position scroller that handles sub-position scrolling but has no
-     * understanding of layout.
-     */
-    abstract class AbsSubPositionScroller extends AbsPositionScroller {
-        private static final int DURATION_AUTO = -1;
-
-        private static final int DURATION_AUTO_MIN = 100;
-        private static final int DURATION_AUTO_MAX = 500;
-
-        private final SubScroller mSubScroller = new SubScroller();
-
-        /**
-         * The target offset in pixels between the top of the list and the top
-         * of the target position.
-         */
-        private int mOffset;
-
-        /**
-         * Scroll the minimum amount to get the target view entirely on-screen.
-         */
-        private void scrollToPosition(final int targetPosition, final boolean useOffset,
-                final int offset, final int boundPosition, final int duration) {
-            stop();
-
-            if (mDataChanged) {
-                // Wait until we're back in a stable state to try this.
-                mPositionScrollAfterLayout = new Runnable() {
-                    @Override
-                    public void run() {
-                        scrollToPosition(
-                                targetPosition, useOffset, offset, boundPosition, duration);
-                    }
-                };
-                return;
-            }
-
-            if (mAdapter == null) {
-                // Can't scroll anywhere without an adapter.
-                return;
-            }
-
-            final int itemCount = getCount();
-            final int clampedPosition = MathUtils.constrain(targetPosition, 0, itemCount - 1);
-            final int clampedBoundPosition = MathUtils.constrain(boundPosition, -1, itemCount - 1);
-            final int firstPosition = getFirstVisiblePosition();
-            final int lastPosition = firstPosition + getChildCount();
-            final int targetRow = getRowForPosition(clampedPosition);
-            final int firstRow = getRowForPosition(firstPosition);
-            final int lastRow = getRowForPosition(lastPosition);
-            if (useOffset || targetRow <= firstRow) {
-                // Offset so the target row is top-aligned.
-                mOffset = offset;
-            } else if (targetRow >= lastRow - 1) {
-                // Offset so the target row is bottom-aligned.
-                final int listHeight = getHeight() - getPaddingTop() - getPaddingBottom();
-                mOffset = getHeightForPosition(clampedPosition) - listHeight;
-            } else {
-                // Don't scroll, target is entirely on-screen.
-                return;
-            }
-
-            float endSubRow = targetRow;
-            if (clampedBoundPosition != INVALID_POSITION) {
-                final int boundRow = getRowForPosition(clampedBoundPosition);
-                if (boundRow >= firstRow && boundRow < lastRow && boundRow != targetRow) {
-                    endSubRow = computeBoundSubRow(targetRow, boundRow);
-                }
-            }
-
-            final View firstChild = getChildAt(0);
-            if (firstChild == null) {
-                return;
-            }
-
-            final int firstChildHeight = firstChild.getHeight();
-            final float startOffsetRatio;
-            if (firstChildHeight == 0) {
-                startOffsetRatio = 0;
-            } else {
-                startOffsetRatio = -firstChild.getTop() / (float) firstChildHeight;
-            }
-
-            final float startSubRow = MathUtils.constrain(
-                    firstRow + startOffsetRatio, 0, getCount());
-            if (startSubRow == endSubRow && mOffset == 0) {
-                // Don't scroll, target is already in position.
-                return;
-            }
-
-            final int durationMillis;
-            if (duration == DURATION_AUTO) {
-                final float subRowDelta = Math.abs(startSubRow - endSubRow);
-                durationMillis = (int) MathUtils.lerp(
-                        DURATION_AUTO_MIN, DURATION_AUTO_MAX, subRowDelta / getCount());
-            } else {
-                durationMillis = duration;
-            }
-
-            mSubScroller.startScroll(startSubRow, endSubRow, durationMillis);
-
-            postOnAnimation(mAnimationFrame);
-        }
-
-        /**
-         * Given a target row and offset, computes the sub-row position that
-         * aligns with the top of the list. If the offset is negative, the
-         * resulting sub-row will be smaller than the target row.
-         */
-        private float resolveOffset(int targetRow, int offset) {
-            // Compute the target sub-row position by finding the actual row
-            // indicated by the target and offset.
-            int remainingOffset = offset;
-            int targetHeight = getHeightForRow(targetRow);
-            if (offset < 0) {
-                // Subtract row heights until we find the right row.
-                while (targetRow > 0 && remainingOffset < 0) {
-                    remainingOffset += targetHeight;
-                    targetRow--;
-                    targetHeight = getHeightForRow(targetRow);
-                }
-            } else if (offset > 0) {
-                // Add row heights until we find the right row.
-                while (targetRow < getCount() - 1 && remainingOffset > targetHeight) {
-                    remainingOffset -= targetHeight;
-                    targetRow++;
-                    targetHeight = getHeightForRow(targetRow);
-                }
-            }
-
-            final float targetOffsetRatio;
-            if (remainingOffset < 0 || targetHeight == 0) {
-                targetOffsetRatio = 0;
-            } else {
-                targetOffsetRatio = remainingOffset / (float) targetHeight;
-            }
-
-            return targetRow + targetOffsetRatio;
-        }
-
-        private float computeBoundSubRow(int targetRow, int boundRow) {
-            final float targetSubRow = resolveOffset(targetRow, mOffset);
-            mOffset = 0;
-
-            // The target row is below the bound row, so the end position would
-            // push the bound position above the list. Abort!
-            if (targetSubRow >= boundRow) {
-                return boundRow;
-            }
-
-            // Compute the closest possible sub-position that wouldn't push the
-            // bound position's view further below the list.
-            final int listHeight = getHeight() - getPaddingTop() - getPaddingBottom();
-            final int boundHeight = getHeightForRow(boundRow);
-            final float boundSubRow = resolveOffset(boundRow, -listHeight + boundHeight);
-
-            return Math.max(boundSubRow, targetSubRow);
-        }
-
-        @Override
-        public void start(int position) {
-            scrollToPosition(position, false, 0, INVALID_POSITION, DURATION_AUTO);
-        }
-
-        @Override
-        public void start(int position, int boundPosition) {
-            scrollToPosition(position, false, 0, boundPosition, DURATION_AUTO);
-        }
-
-        @Override
-        public void startWithOffset(int position, int offset) {
-            scrollToPosition(position, true, offset, INVALID_POSITION, DURATION_AUTO);
-        }
-
-        @Override
-        public void startWithOffset(int position, int offset, int duration) {
-            scrollToPosition(position, true, offset, INVALID_POSITION, duration);
-        }
-
-        @Override
-        public void stop() {
-            removeCallbacks(mAnimationFrame);
-        }
-
-        /**
-         * Returns the height of a row, which is computed as the maximum height of
-         * the items in the row.
-         *
-         * @param row the row index
-         * @return row height in pixels
-         */
-        public abstract int getHeightForRow(int row);
-
-        /**
-         * Returns the row for the specified item position.
-         *
-         * @param position the item position
-         * @return the row index
-         */
-        public abstract int getRowForPosition(int position);
-
-        /**
-         * Returns the first item position within the specified row.
-         *
-         * @param row the row
-         * @return the position of the first item in the row
-         */
-        public abstract int getFirstPositionForRow(int row);
-
-        private void onAnimationFrame() {
-            final boolean shouldPost = mSubScroller.computePosition();
-            final float subRow = mSubScroller.getPosition();
-
-            final int row = (int) subRow;
-            final int position = getFirstPositionForRow(row);
-            if (position >= getCount()) {
-                // Invalid position, abort scrolling.
-                return;
-            }
-
-            final int rowHeight = getHeightForRow(row);
-            final int offset = (int) (rowHeight * (subRow - row));
-            final int addOffset = (int) (mOffset * mSubScroller.getInterpolatedValue());
-            setSelectionFromTop(position, -offset - addOffset);
-
-            if (shouldPost) {
-                postOnAnimation(mAnimationFrame);
-            }
-        }
-
-        private Runnable mAnimationFrame = new Runnable() {
-            @Override
-            public void run() {
-                onAnimationFrame();
-            }
-        };
-    }
-
-    /**
-     * Scroller capable of returning floating point positions.
-     */
-    static class SubScroller {
-        private static final Interpolator INTERPOLATOR = new AccelerateDecelerateInterpolator();
-
-        private float mStartPosition;
-        private float mEndPosition;
-        private long mStartTime;
-        private long mDuration;
-
-        private float mPosition;
-        private float mInterpolatedValue;
-
-        public void startScroll(float startPosition, float endPosition, int duration) {
-            mStartPosition = startPosition;
-            mEndPosition = endPosition;
-            mDuration = duration;
-
-            mStartTime = AnimationUtils.currentAnimationTimeMillis();
-            mPosition = startPosition;
-            mInterpolatedValue = 0;
-        }
-
-        public boolean computePosition() {
-            final long elapsed = AnimationUtils.currentAnimationTimeMillis() - mStartTime;
-            final float value;
-            if (mDuration <= 0) {
-                value = 1;
-            } else {
-                value = MathUtils.constrain(elapsed / (float) mDuration, 0, 1);
-            }
-
-            mInterpolatedValue = INTERPOLATOR.getInterpolation(value);
-            mPosition = (mEndPosition - mStartPosition) * mInterpolatedValue + mStartPosition;
-
-            return elapsed < mDuration;
-        }
-
-        public float getPosition() {
-            return mPosition;
-        }
-
-        public float getInterpolatedValue() {
-            return mInterpolatedValue;
-        }
-    }
 }
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index 93810b3..33cc66e 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -1029,11 +1029,6 @@
     }
 
     @Override
-    AbsPositionScroller createPositionScroller() {
-        return new GridViewPositionScroller();
-    }
-
-    @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         // Sets up mListPadding
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -2392,33 +2387,4 @@
                 column, 1, row, 1, isHeading, isSelected);
         info.setCollectionItemInfo(itemInfo);
     }
-
-    /**
-     * Sub-position scroller that understands the layout of a GridView.
-     */
-    class GridViewPositionScroller extends AbsSubPositionScroller {
-        @Override
-        public int getRowForPosition(int position) {
-            return position / mNumColumns;
-        }
-
-        @Override
-        public int getFirstPositionForRow(int row) {
-            return row * mNumColumns;
-        }
-
-        @Override
-        public int getHeightForRow(int row) {
-            final int firstRowPosition = row * mNumColumns;
-            final int lastRowPosition = Math.min(getCount(), firstRowPosition + mNumColumns);
-            int maxHeight = 0;
-            for (int i = firstRowPosition; i < lastRowPosition; i++) {
-                final int height = getHeightForPosition(i);
-                if (height > maxHeight) {
-                    maxHeight = height;
-                }
-            }
-            return maxHeight;
-        }
-    }
 }
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 1baeca8..9db1e05 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -3872,11 +3872,6 @@
     }
 
     @Override
-    AbsPositionScroller createPositionScroller() {
-        return new ListViewPositionScroller();
-    }
-
-    @Override
     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
         super.onInitializeAccessibilityEvent(event);
         event.setClassName(ListView.class.getName());
@@ -3905,24 +3900,4 @@
                 0, 1, position, 1, isHeading, isSelected);
         info.setCollectionItemInfo(itemInfo);
     }
-
-    /**
-     * Sub-position scroller that understands the layout of a ListView.
-     */
-    class ListViewPositionScroller extends AbsSubPositionScroller {
-        @Override
-        public int getRowForPosition(int position) {
-            return position;
-        }
-
-        @Override
-        public int getFirstPositionForRow(int row) {
-            return row;
-        }
-
-        @Override
-        public int getHeightForRow(int row) {
-            return getHeightForPosition(row);
-        }
-    }
 }
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index a2cc40c..b524177 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1257,4 +1257,11 @@
     <instrumentation android:name="android.test.InstrumentationTestRunner"
             android:targetPackage="com.android.frameworks.coretests"
             android:label="Frameworks Core Tests" />
+    <key-sets>
+        <key-set android:name="A" >
+          <public-key android:name="keyA"
+                      android:value="MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJoN1Nsgqf0V4C/bbN8wo8O2X/S5D76+5Mb9mlIsHkUTUTbHCNk+LxHIUYLm89YbP9zImrV0bUHLUAZUyoMUCiMCAwEAAQ=="/>
+        </key-set>
+        <upgrade-key-set android:name="A"/>
+    </key-sets>
 </manifest>
diff --git a/core/tests/coretests/apks/keyset/Android.mk b/core/tests/coretests/apks/keyset/Android.mk
index e44ac6c..306dc90 100644
--- a/core/tests/coretests/apks/keyset/Android.mk
+++ b/core/tests/coretests/apks/keyset/Android.mk
@@ -88,4 +88,21 @@
 LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_A
 LOCAL_ADDITIONAL_CERTIFICATES := $(LOCAL_PATH)/../../certs/keyset_B
 LOCAL_MANIFEST_FILE := uB/AndroidManifest.xml
+include $(FrameworkCoreTests_BUILD_PACKAGE)
+
+#apks signed by platform only
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_PACKAGE_NAME := keyset_splat_api
+LOCAL_CERTIFICATE := platform
+LOCAL_MANIFEST_FILE := api_test/AndroidManifest.xml
+include $(FrameworkCoreTests_BUILD_PACKAGE)
+
+#apks signed by platform and keyset_A
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_PACKAGE_NAME := keyset_splata_api
+LOCAL_CERTIFICATE := platform
+LOCAL_ADDITIONAL_CERTIFICATES := $(LOCAL_PATH)/../../certs/keyset_A
+LOCAL_MANIFEST_FILE := api_test/AndroidManifest.xml
 include $(FrameworkCoreTests_BUILD_PACKAGE)
\ No newline at end of file
diff --git a/core/tests/coretests/apks/keyset/api_test/AndroidManifest.xml b/core/tests/coretests/apks/keyset/api_test/AndroidManifest.xml
new file mode 100644
index 0000000..4c7e968
--- /dev/null
+++ b/core/tests/coretests/apks/keyset/api_test/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.frameworks.coretests.keysets_api">
+    <application android:hasCode="false">
+    </application>
+</manifest>
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index 0244425..3a80309 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -26,6 +26,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.KeySet;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -33,6 +34,7 @@
 import android.content.res.Resources;
 import android.content.res.Resources.NotFoundException;
 import android.net.Uri;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.FileUtils;
@@ -3328,6 +3330,174 @@
     }
 
     /**
+     * The following tests are related to testing KeySets-based API
+     */
+
+    /*
+     * testGetSigningKeySetNull - ensure getSigningKeySet() returns null on null
+     * input and when calling a package other than that which made the call.
+     */
+    public void testGetSigningKeySet() throws Exception {
+        PackageManager pm = getPm();
+        String mPkgName = mContext.getPackageName();
+        String otherPkgName = "com.android.frameworks.coretests.keysets_api";
+        KeySet ks;
+        try {
+            ks = pm.getSigningKeySet(null);
+            assertTrue(false); // should have thrown
+        } catch (NullPointerException e) {
+        }
+        try {
+            ks = pm.getSigningKeySet("keysets.test.bogus.package");
+            assertTrue(false); // should have thrown
+        } catch (IllegalArgumentException e) {
+        }
+        installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api,
+                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        try {
+            ks = pm.getSigningKeySet(otherPkgName);
+            assertTrue(false); // should have thrown
+        } catch (SecurityException e) {
+        }
+        cleanUpInstall(otherPkgName);
+        ks = pm.getSigningKeySet(mContext.getPackageName());
+        assertNotNull(ks);
+    }
+
+    /*
+     * testGetKeySetByAlias - same as getSigningKeySet, but for keysets defined
+     * by this package.
+     */
+    public void testGetKeySetByAlias() throws Exception {
+        PackageManager pm = getPm();
+        String mPkgName = mContext.getPackageName();
+        String otherPkgName = "com.android.frameworks.coretests.keysets_api";
+        KeySet ks;
+        try {
+            ks = pm.getKeySetByAlias(null, null);
+            assertTrue(false); // should have thrown
+        } catch (NullPointerException e) {
+        }
+        try {
+            ks = pm.getKeySetByAlias(null, "keysetBogus");
+            assertTrue(false); // should have thrown
+        } catch (NullPointerException e) {
+        }
+        try {
+            ks = pm.getKeySetByAlias("keysets.test.bogus.package", null);
+            assertTrue(false); // should have thrown
+        } catch (NullPointerException e) {
+        }
+        try {
+            ks = pm.getKeySetByAlias("keysets.test.bogus.package", "A");
+            assertTrue(false); // should have thrown
+        } catch(IllegalArgumentException e) {
+        }
+        try {
+            ks = pm.getKeySetByAlias(mPkgName, "keysetBogus");
+            assertTrue(false); // should have thrown
+        } catch(IllegalArgumentException e) {
+        }
+        installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api,
+                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        try {
+            ks = pm.getKeySetByAlias(otherPkgName, "A");
+            assertTrue(false); // should have thrown
+        } catch (SecurityException e) {
+        }
+        cleanUpInstall(otherPkgName);
+        ks = pm.getKeySetByAlias(mPkgName, "A");
+        assertNotNull(ks);
+    }
+
+    public void testIsSignedBy() throws Exception {
+        PackageManager pm = getPm();
+        String mPkgName = mContext.getPackageName();
+        String otherPkgName = "com.android.frameworks.coretests.keysets_api";
+        KeySet mSigningKS = pm.getSigningKeySet(mPkgName);
+        KeySet mDefinedKS = pm.getKeySetByAlias(mPkgName, "A");
+
+        try {
+            assertFalse(pm.isSignedBy(null, null));
+            assertTrue(false); // should have thrown
+        } catch (NullPointerException e) {
+        }
+        try {
+            assertFalse(pm.isSignedBy(null, mSigningKS));
+            assertTrue(false); // should have thrown
+        } catch (NullPointerException e) {
+        }
+        try {
+            assertFalse(pm.isSignedBy(mPkgName, null));
+            assertTrue(false); // should have thrown
+        } catch (NullPointerException e) {
+        }
+        try {
+            assertFalse(pm.isSignedBy("keysets.test.bogus.package", mDefinedKS));
+        } catch(IllegalArgumentException e) {
+        }
+        assertFalse(pm.isSignedBy(mPkgName, mDefinedKS));
+        assertFalse(pm.isSignedBy(mPkgName, new KeySet(new Binder())));
+        assertTrue(pm.isSignedBy(mPkgName, mSigningKS));
+
+        installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api,
+                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        assertFalse(pm.isSignedBy(otherPkgName, mDefinedKS));
+        assertTrue(pm.isSignedBy(otherPkgName, mSigningKS));
+        cleanUpInstall(otherPkgName);
+
+        installFromRawResource("keysetApi.apk", R.raw.keyset_splata_api,
+                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        assertTrue(pm.isSignedBy(otherPkgName, mDefinedKS));
+        assertTrue(pm.isSignedBy(otherPkgName, mSigningKS));
+        cleanUpInstall(otherPkgName);
+    }
+
+    public void testIsSignedByExactly() throws Exception {
+        PackageManager pm = getPm();
+        String mPkgName = mContext.getPackageName();
+        String otherPkgName = "com.android.frameworks.coretests.keysets_api";
+        KeySet mSigningKS = pm.getSigningKeySet(mPkgName);
+        KeySet mDefinedKS = pm.getKeySetByAlias(mPkgName, "A");
+        try {
+            assertFalse(pm.isSignedBy(null, null));
+            assertTrue(false); // should have thrown
+        } catch (NullPointerException e) {
+        }
+        try {
+            assertFalse(pm.isSignedBy(null, mSigningKS));
+            assertTrue(false); // should have thrown
+        } catch (NullPointerException e) {
+        }
+        try {
+            assertFalse(pm.isSignedBy(mPkgName, null));
+            assertTrue(false); // should have thrown
+        } catch (NullPointerException e) {
+        }
+        try {
+            assertFalse(pm.isSignedByExactly("keysets.test.bogus.package", mDefinedKS));
+        } catch(IllegalArgumentException e) {
+        }
+        assertFalse(pm.isSignedByExactly(mPkgName, mDefinedKS));
+        assertFalse(pm.isSignedByExactly(mPkgName, new KeySet(new Binder())));
+        assertTrue(pm.isSignedByExactly(mPkgName, mSigningKS));
+
+        installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api,
+                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        assertFalse(pm.isSignedByExactly(otherPkgName, mDefinedKS));
+        assertTrue(pm.isSignedByExactly(otherPkgName, mSigningKS));
+        cleanUpInstall(otherPkgName);
+
+        installFromRawResource("keysetApi.apk", R.raw.keyset_splata_api,
+                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        assertFalse(pm.isSignedByExactly(otherPkgName, mDefinedKS));
+        assertFalse(pm.isSignedByExactly(otherPkgName, mSigningKS));
+        cleanUpInstall(otherPkgName);
+    }
+
+
+
+    /**
      * The following tests are related to testing the checkSignatures api.
      */
     private void checkSignatures(int apk1, int apk2, int expMatchResult) throws Exception {
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index eb6bf2c..c65961d 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2912,6 +2912,20 @@
     }
 
     /**
+     * Notify audio manager about volume controller visibility changes.
+     * Currently limited to SystemUI.
+     *
+     * @hide
+     */
+    public void notifyVolumeControllerVisible(IVolumeController controller, boolean visible) {
+        try {
+            getService().notifyVolumeControllerVisible(controller, visible);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Error notifying about volume controller visibility", e);
+        }
+    }
+
+    /**
      * Only useful for volume controllers.
      * @hide
      */
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index b08d631..ab63145 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -61,6 +61,7 @@
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.Vibrator;
@@ -810,6 +811,9 @@
 
         // Restore the default media button receiver from the system settings
         mMediaFocusControl.restoreMediaButtonReceiver();
+
+        // Load settings for the volume controller
+        mVolumeController.loadSettings(cr);
     }
 
     private int rescaleIndex(int index, int srcStream, int dstStream) {
@@ -851,14 +855,23 @@
         } else {
             streamType = getActiveStreamType(suggestedStreamType);
         }
+        final int resolvedStream = mStreamVolumeAlias[streamType];
 
         // Play sounds on STREAM_RING and STREAM_REMOTE_MUSIC only.
         if ((streamType != STREAM_REMOTE_MUSIC) &&
                 (flags & AudioManager.FLAG_PLAY_SOUND) != 0 &&
-                (mStreamVolumeAlias[streamType] != AudioSystem.STREAM_RING)) {
+                resolvedStream != AudioSystem.STREAM_RING) {
             flags &= ~AudioManager.FLAG_PLAY_SOUND;
         }
 
+        // For notifications/ring, show the ui before making any adjustments
+        if (mVolumeController.suppressAdjustment(resolvedStream, flags)) {
+            direction = 0;
+            flags &= ~AudioManager.FLAG_PLAY_SOUND;
+            flags &= ~AudioManager.FLAG_VIBRATE;
+            if (DEBUG_VOL) Log.d(TAG, "Volume controller suppressed adjustment");
+        }
+
         if (streamType == STREAM_REMOTE_MUSIC) {
             // TODO bounce it to MediaSessionService to find an appropriate
             // session
@@ -4955,15 +4968,65 @@
             }
         }
         mVolumeController.setController(controller);
+        if (DEBUG_VOL) Log.d(TAG, "Volume controller: " + mVolumeController);
+    }
+
+    @Override
+    public void notifyVolumeControllerVisible(final IVolumeController controller, boolean visible) {
+        enforceSelfOrSystemUI("notify about volume controller visibility");
+
+        // return early if the controller is not current
+        if (!mVolumeController.isSameBinder(controller)) {
+            return;
+        }
+
+        mVolumeController.setVisible(visible);
+        if (DEBUG_VOL) Log.d(TAG, "Volume controller visible: " + visible);
     }
 
     public static class VolumeController {
         private static final String TAG = "VolumeController";
 
         private IVolumeController mController;
+        private boolean mVisible;
+        private long mNextLongPress;
+        private int mLongPressTimeout;
 
         public void setController(IVolumeController controller) {
             mController = controller;
+            mVisible = false;
+        }
+
+        public void loadSettings(ContentResolver cr) {
+            mLongPressTimeout = Settings.Secure.getIntForUser(cr,
+                    Settings.Secure.LONG_PRESS_TIMEOUT, 500, UserHandle.USER_CURRENT);
+        }
+
+        public boolean suppressAdjustment(int resolvedStream, int flags) {
+            boolean suppress = false;
+            if (resolvedStream == AudioSystem.STREAM_RING && mController != null) {
+                final long now = SystemClock.uptimeMillis();
+                if ((flags & AudioManager.FLAG_SHOW_UI) != 0 && !mVisible) {
+                    // ui will become visible
+                    if (mNextLongPress < now) {
+                        mNextLongPress = now + mLongPressTimeout;
+                    }
+                    suppress = true;
+                } else if (mNextLongPress > 0) {  // in a long-press
+                    if (now > mNextLongPress) {
+                        // long press triggered, no more suppression
+                        mNextLongPress = 0;
+                    } else {
+                        // keep suppressing until the long press triggers
+                        suppress = true;
+                    }
+                }
+            }
+            return suppress;
+        }
+
+        public void setVisible(boolean visible) {
+            mVisible = visible;
         }
 
         public boolean isSameBinder(IVolumeController controller) {
@@ -4980,7 +5043,7 @@
 
         @Override
         public String toString() {
-            return "VolumeController(" + asBinder() + ")";
+            return "VolumeController(" + asBinder() + ",mVisible=" + mVisible + ")";
         }
 
         public void postDisplaySafeVolumeWarning(int flags) {
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index e112a65..4f7021e 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -195,6 +195,8 @@
 
     void setVolumeController(in IVolumeController controller);
 
+    void notifyVolumeControllerVisible(in IVolumeController controller, boolean visible);
+
     boolean isStreamAffectedByRingerMode(int streamType);
 
     void disableSafeMediaVolume();
diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java
index 5d1a7e0..11f7720 100644
--- a/media/java/android/media/session/MediaSessionLegacyHelper.java
+++ b/media/java/android/media/session/MediaSessionLegacyHelper.java
@@ -202,7 +202,7 @@
                 if (up) {
                     flags = AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE;
                 } else {
-                    flags = AudioManager.FLAG_SHOW_UI;
+                    flags = AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE;
                 }
             }
 
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
index e4716da..8a65a2e 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
@@ -27,6 +27,7 @@
 import android.print.PrinterId;
 import android.print.PrinterInfo;
 import android.printservice.PrintServiceInfo;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
@@ -68,10 +69,10 @@
     private static final int MAX_FAVORITE_PRINTER_COUNT = 4;
 
     private final List<PrinterInfo> mPrinters =
-            new ArrayList<PrinterInfo>();
+            new ArrayList<>();
 
     private final List<PrinterInfo> mFavoritePrinters =
-            new ArrayList<PrinterInfo>();
+            new ArrayList<>();
 
     private final PersistenceManager mPersistenceManager;
 
@@ -92,7 +93,7 @@
 
     private void computeAndDeliverResult(ArrayMap<PrinterId, PrinterInfo> discoveredPrinters,
             ArrayMap<PrinterId, PrinterInfo> favoritePrinters) {
-        List<PrinterInfo> printers = new ArrayList<PrinterInfo>();
+        List<PrinterInfo> printers = new ArrayList<>();
 
         // Add the updated favorite printers.
         final int favoritePrinterCount = favoritePrinters.size();
@@ -142,7 +143,7 @@
         // The contract is that if we already have a valid,
         // result the we have to deliver it immediately.
         if (!mPrinters.isEmpty()) {
-            deliverResult(new ArrayList<PrinterInfo>(mPrinters));
+            deliverResult(new ArrayList<>(mPrinters));
         }
         // Always load the data to ensure discovery period is
         // started and to make sure obsolete printers are updated.
@@ -184,11 +185,12 @@
                                 + mDiscoverySession.getPrinters().size()
                                 + " " + FusedPrintersProvider.this.hashCode());
                     }
+
                     updatePrinters(mDiscoverySession.getPrinters(), mFavoritePrinters);
                 }
             });
             final int favoriteCount = mFavoritePrinters.size();
-            List<PrinterId> printerIds = new ArrayList<PrinterId>(favoriteCount);
+            List<PrinterId> printerIds = new ArrayList<>(favoriteCount);
             for (int i = 0; i < favoriteCount; i++) {
                 printerIds.add(mFavoritePrinters.get(i).getId());
             }
@@ -208,16 +210,19 @@
 
         mPrintersUpdatedBefore = true;
 
-        ArrayMap<PrinterId, PrinterInfo> printersMap =
-                new ArrayMap<PrinterId, PrinterInfo>();
+        // Some of the found printers may have be a printer that is in the
+        // history but with its name changed. Hence, we try to update the
+        // printer to use its current name instead of the historical one.
+        mPersistenceManager.updatePrintersHistoricalNamesIfNeeded(printers);
+
+        ArrayMap<PrinterId, PrinterInfo> printersMap = new ArrayMap<>();
         final int printerCount = printers.size();
         for (int i = 0; i < printerCount; i++) {
             PrinterInfo printer = printers.get(i);
             printersMap.put(printer.getId(), printer);
         }
 
-        ArrayMap<PrinterId, PrinterInfo> favoritePrintersMap =
-                new ArrayMap<PrinterId, PrinterInfo>();
+        ArrayMap<PrinterId, PrinterInfo> favoritePrintersMap = new ArrayMap<>();
         final int favoritePrinterCount = favoritePrinters.size();
         for (int i = 0; i < favoritePrinterCount; i++) {
             PrinterInfo favoritePrinter = favoritePrinters.get(i);
@@ -310,7 +315,7 @@
         for (int i = 0; i < favoritePrinterCount; i++) {
             PrinterInfo favoritePrinter = mFavoritePrinters.get(i);
             if (favoritePrinter.getId().equals(printerId)) {
-                newFavoritePrinters = new ArrayList<PrinterInfo>();
+                newFavoritePrinters = new ArrayList<>();
                 newFavoritePrinters.addAll(mPrinters);
                 newFavoritePrinters.remove(i);
                 break;
@@ -344,7 +349,7 @@
 
         private final AtomicFile mStatePersistFile;
 
-        private List<PrinterInfo> mHistoricalPrinters = new ArrayList<PrinterInfo>();
+        private List<PrinterInfo> mHistoricalPrinters = new ArrayList<>();
 
         private boolean mReadHistoryCompleted;
         private boolean mReadHistoryInProgress;
@@ -382,17 +387,42 @@
             mReadTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
         }
 
-        @SuppressWarnings("unchecked")
+        public void updatePrintersHistoricalNamesIfNeeded(List<PrinterInfo> printers) {
+            boolean writeHistory = false;
+
+            final int printerCount = printers.size();
+            for (int i = 0; i < printerCount; i++) {
+                PrinterInfo printer = printers.get(i);
+                writeHistory |= renamePrinterIfNeeded(printer);
+            }
+
+            if (writeHistory) {
+                writePrinterHistory();
+            }
+        }
+
+        public boolean renamePrinterIfNeeded(PrinterInfo printer) {
+            boolean renamed = false;
+            final int printerCount = mHistoricalPrinters.size();
+            for (int i = 0; i < printerCount; i++) {
+                PrinterInfo historicalPrinter = mHistoricalPrinters.get(i);
+                if (historicalPrinter.getId().equals(printer.getId())
+                        && !TextUtils.equals(historicalPrinter.getName(), printer.getName())) {
+                    mHistoricalPrinters.set(i, printer);
+                    renamed = true;
+                }
+            }
+            return renamed;
+        }
+
         public void addPrinterAndWritePrinterHistory(PrinterInfo printer) {
             if (mHistoricalPrinters.size() >= MAX_HISTORY_LENGTH) {
                 mHistoricalPrinters.remove(0);
             }
             mHistoricalPrinters.add(printer);
-            new WriteTask().executeOnExecutor(AsyncTask.SERIAL_EXECUTOR,
-                    new ArrayList<PrinterInfo>(mHistoricalPrinters));
+            writePrinterHistory();
         }
 
-        @SuppressWarnings("unchecked")
         public void removeHistoricalPrinterAndWritePrinterHistory(PrinterId printerId) {
             boolean writeHistory = false;
             final int printerCount = mHistoricalPrinters.size();
@@ -404,18 +434,22 @@
                 }
             }
             if (writeHistory) {
-                new WriteTask().executeOnExecutor(AsyncTask.SERIAL_EXECUTOR,
-                        new ArrayList<PrinterInfo>(mHistoricalPrinters));
+                writePrinterHistory();
             }
         }
 
+        @SuppressWarnings("unchecked")
+        private void writePrinterHistory() {
+            new WriteTask().executeOnExecutor(AsyncTask.SERIAL_EXECUTOR,
+                    new ArrayList<>(mHistoricalPrinters));
+        }
+
         public boolean isHistoryChanged() {
             return mLastReadHistoryTimestamp != mStatePersistFile.getBaseFile().lastModified();
         }
 
         private List<PrinterInfo> computeFavoritePrinters(List<PrinterInfo> printers) {
-            Map<PrinterId, PrinterRecord> recordMap =
-                    new ArrayMap<PrinterId, PrinterRecord>();
+            Map<PrinterId, PrinterRecord> recordMap = new ArrayMap<>();
 
             // Recompute the weights.
             float currentWeight = 1.0f;
@@ -433,14 +467,14 @@
             }
 
             // Soft the favorite printers.
-            List<PrinterRecord> favoriteRecords = new ArrayList<PrinterRecord>(
+            List<PrinterRecord> favoriteRecords = new ArrayList<>(
                     recordMap.values());
             Collections.sort(favoriteRecords);
 
             // Write the favorites to the output.
             final int favoriteCount = Math.min(favoriteRecords.size(),
                     MAX_FAVORITE_PRINTER_COUNT);
-            List<PrinterInfo> favoritePrinters = new ArrayList<PrinterInfo>(favoriteCount);
+            List<PrinterInfo> favoritePrinters = new ArrayList<>(favoriteCount);
             for (int i = 0; i < favoriteCount; i++) {
                 PrinterInfo printer = favoriteRecords.get(i).printer;
                 favoritePrinters.add(printer);
@@ -482,7 +516,7 @@
                 List<PrintServiceInfo> services = printManager
                         .getEnabledPrintServices();
 
-                Set<ComponentName> enabledComponents = new ArraySet<ComponentName>();
+                Set<ComponentName> enabledComponents = new ArraySet<>();
                 final int installedServiceCount = services.size();
                 for (int i = 0; i < installedServiceCount; i++) {
                     ServiceInfo serviceInfo = services.get(i).getResolveInfo().serviceInfo;
@@ -528,28 +562,23 @@
                         Log.i(LOG_TAG, "No existing printer history "
                                 + FusedPrintersProvider.this.hashCode());
                     }
-                    return new ArrayList<PrinterInfo>();
+                    return new ArrayList<>();
                 }
                 try {
-                    List<PrinterInfo> printers = new ArrayList<PrinterInfo>();
+                    List<PrinterInfo> printers = new ArrayList<>();
                     XmlPullParser parser = Xml.newPullParser();
                     parser.setInput(in, null);
                     parseState(parser, printers);
                     // Take a note which version of the history was read.
                     mLastReadHistoryTimestamp = mStatePersistFile.getBaseFile().lastModified();
                     return printers;
-                } catch (IllegalStateException ise) {
-                    Slog.w(LOG_TAG, "Failed parsing ", ise);
-                } catch (NullPointerException npe) {
-                    Slog.w(LOG_TAG, "Failed parsing ", npe);
-                } catch (NumberFormatException nfe) {
-                    Slog.w(LOG_TAG, "Failed parsing ", nfe);
-                } catch (XmlPullParserException xppe) {
-                    Slog.w(LOG_TAG, "Failed parsing ", xppe);
-                } catch (IOException ioe) {
-                    Slog.w(LOG_TAG, "Failed parsing ", ioe);
-                } catch (IndexOutOfBoundsException iobe) {
-                    Slog.w(LOG_TAG, "Failed parsing ", iobe);
+                } catch (IllegalStateException
+                        | NullPointerException
+                        | NumberFormatException
+                        | XmlPullParserException
+                        | IOException
+                        | IndexOutOfBoundsException e) {
+                    Slog.w(LOG_TAG, "Failed parsing ", e);
                 } finally {
                     IoUtils.closeQuietly(in);
                 }
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
index eaf268d..094edf8 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
@@ -389,6 +389,7 @@
             mSelectedPages = selectedPages;
             mSelectedPageCount = PageRangeUtils.getNormalizedPageCount(
                     mSelectedPages, mDocumentPageCount);
+            updatePreviewAreaAndPageSize();
             notifyDataSetChanged();
         }
         return mSelectedPages;
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index b79dbbe..d4feccd 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -516,6 +516,10 @@
     <string name="quick_settings_time_label">Time</string>
     <!-- QuickSettings: User [CHAR LIMIT=NONE] -->
     <string name="quick_settings_user_label">Me</string>
+    <!-- QuickSettings: Title of the user detail panel [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_user_title">User</string>
+    <!-- QuickSettings: Label on the item for adding a new user [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_user_new_user">New user</string>
     <!-- QuickSettings: Wifi [CHAR LIMIT=NONE] -->
     <string name="quick_settings_wifi_label">Wi-Fi</string>
     <!-- QuickSettings: Wifi (Not connected) [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index 8a80b76..e7ac2e1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -32,7 +32,7 @@
             // Enables the filtering of tasks according to their grouping
             public static final boolean EnableTaskFiltering = false;
             // Enables clipping of tasks against each other
-            public static final boolean EnableTaskStackClipping = true;
+            public static final boolean EnableTaskStackClipping = false;
             // Enables tapping on the TaskBar to launch the task
             public static final boolean EnableTaskBarTouchEvents = true;
             // Enables app-info pane on long-pressing the icon
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 56de0be..1e581c1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -458,8 +458,6 @@
         filter.addAction(ACTION_TOGGLE_RECENTS_ACTIVITY);
         filter.addAction(ACTION_START_ENTER_ANIMATION);
         registerReceiver(mServiceBroadcastReceiver, filter);
-
-        mVisible = true;
     }
 
     @Override
@@ -485,6 +483,8 @@
                 }
             }, 1);
         }
+
+        mVisible = true;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
index 31825af..4c0ff48 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
@@ -72,7 +72,12 @@
 
     /** Adds a runnable to the last-decrement runnables list. */
     public void addLastDecrementRunnable(Runnable r) {
+        // To ensure that the last decrement always calls, we increment and decrement after setting
+        // the last decrement runnable
+        boolean ensureLastDecrement = (mCount == 0);
+        if (ensureLastDecrement) increment();
         mLastDecRunnables.add(r);
+        if (ensureLastDecrement) decrement();
     }
 
     /** Decrements the ref count */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
index bda195b..607e155 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
@@ -16,16 +16,10 @@
 
 package com.android.systemui.recents.misc;
 
-import android.app.ActivityManager;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
 import android.graphics.Color;
 import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.ParcelFileDescriptor;
 import com.android.systemui.recents.RecentsConfiguration;
 
-import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 
@@ -73,22 +67,25 @@
         }
     }
 
-    /** Calculates the luminance-preserved greyscale of a given color. */
-    public static int colorToGreyscale(int color) {
-        return Math.round(0.2126f * Color.red(color) + 0.7152f * Color.green(color) +
-                0.0722f * Color.blue(color));
-    }
+    /** Calculates the constrast between two colors, using the algorithm provided by the WCAG v2. */
+    public static float computeContrastBetweenColors(int bg, int fg) {
+        float bgR = Color.red(bg) / 255f;
+        float bgG = Color.green(bg) / 255f;
+        float bgB = Color.blue(bg) / 255f;
+        bgR = (bgR < 0.03928f) ? bgR / 12.92f : (float) Math.pow((bgR + 0.055f) / 1.055f, 2.4f);
+        bgG = (bgG < 0.03928f) ? bgG / 12.92f : (float) Math.pow((bgG + 0.055f) / 1.055f, 2.4f);
+        bgB = (bgB < 0.03928f) ? bgB / 12.92f : (float) Math.pow((bgB + 0.055f) / 1.055f, 2.4f);
+        float bgL = 0.2126f * bgR + 0.7152f * bgG + 0.0722f * bgB;
+        
+        float fgR = Color.red(fg) / 255f;
+        float fgG = Color.green(fg) / 255f;
+        float fgB = Color.blue(fg) / 255f;
+        fgR = (fgR < 0.03928f) ? fgR / 12.92f : (float) Math.pow((fgR + 0.055f) / 1.055f, 2.4f);
+        fgG = (fgG < 0.03928f) ? fgG / 12.92f : (float) Math.pow((fgG + 0.055f) / 1.055f, 2.4f);
+        fgB = (fgB < 0.03928f) ? fgB / 12.92f : (float) Math.pow((fgB + 0.055f) / 1.055f, 2.4f);
+        float fgL = 0.2126f * fgR + 0.7152f * fgG + 0.0722f * fgB;
 
-    /** Returns the ideal color to draw on top of a specified background color. */
-    public static int getIdealColorForBackgroundColorGreyscale(int greyscale, int lightRes,
-                                                               int darkRes) {
-        return (greyscale < 128) ? lightRes : darkRes;
-    }
-    /** Returns the ideal drawable to draw on top of a specified background color. */
-    public static Drawable getIdealResourceForBackgroundColorGreyscale(int greyscale,
-                                                                       Drawable lightRes,
-                                                                       Drawable darkRes) {
-        return (greyscale < 128) ? lightRes : darkRes;
+        return Math.abs((fgL + 0.05f) / (bgL + 0.05f));
     }
 
     /** Sets some private shadow properties. */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/BitmapLruCache.java b/packages/SystemUI/src/com/android/systemui/recents/model/BitmapLruCache.java
index 1344729..757c07f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/BitmapLruCache.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/BitmapLruCache.java
@@ -29,6 +29,6 @@
     @Override
     protected int computeSize(Bitmap b) {
         // The cache size will be measured in kilobytes rather than number of items
-        return b.getAllocationByteCount() / 1024;
+        return b.getAllocationByteCount();
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/DrawableLruCache.java b/packages/SystemUI/src/com/android/systemui/recents/model/DrawableLruCache.java
index 61d19da..5b50358 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/DrawableLruCache.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/DrawableLruCache.java
@@ -31,6 +31,6 @@
         // The cache size will be measured in kilobytes rather than number of items
         // NOTE: this isn't actually correct, as the icon may be smaller
         int maxBytes = (d.getIntrinsicWidth() * d.getIntrinsicHeight() * 4);
-        return maxBytes / 1024;
+        return maxBytes;
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/KeyStoreLruCache.java b/packages/SystemUI/src/com/android/systemui/recents/model/KeyStoreLruCache.java
index 3ccca9a..5f4fabe 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/KeyStoreLruCache.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/KeyStoreLruCache.java
@@ -73,11 +73,6 @@
         return mCache.get(key);
     }
 
-    /** Gets the previous task key that matches the specified key. */
-    final Task.TaskKey getKey(Task.TaskKey key) {
-        return mKeys.get(key);
-    }
-
     /** Puts an entry in the cache for a specific key. */
     final void put(Task.TaskKey key, V value) {
         mCache.put(key, value);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
index 2d50659..2f1c1c4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
@@ -45,7 +45,7 @@
         mSystemServicesProxy = new SystemServicesProxy(context);
         mCb = cb;
         try {
-            register(context, Looper.getMainLooper(), false);
+            register(context, Looper.getMainLooper(), true);
         } catch (IllegalStateException e) {
             e.printStackTrace();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index 854ea1c..cbb8892 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -27,38 +27,29 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.UserHandle;
-import android.util.Pair;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.misc.Console;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 
-import java.util.ArrayList;
 import java.util.Collections;
+import java.util.LinkedHashSet;
 import java.util.List;
-import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentLinkedQueue;
 
 
 /** A bitmap load queue */
 class TaskResourceLoadQueue {
     ConcurrentLinkedQueue<Task> mQueue = new ConcurrentLinkedQueue<Task>();
-    ConcurrentHashMap<Task.TaskKey, Boolean> mForceLoadSet =
-            new ConcurrentHashMap<Task.TaskKey, Boolean>();
-
-    static final Boolean sFalse = new Boolean(false);
 
     /** Adds a new task to the load queue */
-    void addTask(Task t, boolean forceLoad) {
+    void addTask(Task t) {
         if (Console.Enabled) {
             Console.log(Constants.Log.App.TaskDataLoader, "  [TaskResourceLoadQueue|addTask]");
         }
         if (!mQueue.contains(t)) {
             mQueue.add(t);
         }
-        if (forceLoad) {
-            mForceLoadSet.put(t.key, new Boolean(true));
-        }
         synchronized(this) {
             notifyAll();
         }
@@ -68,19 +59,11 @@
      * Retrieves the next task from the load queue, as well as whether we want that task to be
      * force reloaded.
      */
-    Pair<Task, Boolean> nextTask() {
+    Task nextTask() {
         if (Console.Enabled) {
             Console.log(Constants.Log.App.TaskDataLoader, "  [TaskResourceLoadQueue|nextTask]");
         }
-        Task task = mQueue.poll();
-        Boolean forceLoadTask = null;
-        if (task != null) {
-            forceLoadTask = mForceLoadSet.remove(task.key);
-        }
-        if (forceLoadTask == null) {
-            forceLoadTask = sFalse;
-        }
-        return new Pair<Task, Boolean>(task, forceLoadTask);
+        return mQueue.poll();
     }
 
     /** Removes a task from the load queue */
@@ -89,7 +72,6 @@
             Console.log(Constants.Log.App.TaskDataLoader, "  [TaskResourceLoadQueue|removeTask]");
         }
         mQueue.remove(t);
-        mForceLoadSet.remove(t.key);
     }
 
     /** Clears all the tasks from the load queue */
@@ -98,7 +80,6 @@
             Console.log(Constants.Log.App.TaskDataLoader, "  [TaskResourceLoadQueue|clearTasks]");
         }
         mQueue.clear();
-        mForceLoadSet.clear();
     }
 
     /** Returns whether the load queue is empty */
@@ -119,19 +100,20 @@
     DrawableLruCache mApplicationIconCache;
     BitmapLruCache mThumbnailCache;
     Bitmap mDefaultThumbnail;
+    BitmapDrawable mDefaultApplicationIcon;
 
     boolean mCancelled;
     boolean mWaitingOnLoadQueue;
 
     /** Constructor, creates a new loading thread that loads task resources in the background */
-    public TaskResourceLoader(TaskResourceLoadQueue loadQueue,
-                              DrawableLruCache applicationIconCache,
-                              BitmapLruCache thumbnailCache,
-                              Bitmap defaultThumbnail) {
+    public TaskResourceLoader(TaskResourceLoadQueue loadQueue, DrawableLruCache applicationIconCache,
+                              BitmapLruCache thumbnailCache, Bitmap defaultThumbnail,
+                              BitmapDrawable defaultApplicationIcon) {
         mLoadQueue = loadQueue;
         mApplicationIconCache = applicationIconCache;
         mThumbnailCache = thumbnailCache;
         mDefaultThumbnail = defaultThumbnail;
+        mDefaultApplicationIcon = defaultApplicationIcon;
         mMainThreadHandler = new Handler();
         mLoadThread = new HandlerThread("Recents-TaskResourceLoader");
         mLoadThread.setPriority(Thread.NORM_PRIORITY - 1);
@@ -200,59 +182,51 @@
                 SystemServicesProxy ssp = mSystemServicesProxy;
 
                 // Load the next item from the queue
-                Pair<Task, Boolean> nextTaskData = mLoadQueue.nextTask();
-                final Task t = nextTaskData.first;
-                final boolean forceLoadTask = nextTaskData.second;
+                final Task t = mLoadQueue.nextTask();
                 if (t != null) {
-                    Drawable loadIcon = mApplicationIconCache.getCheckLastActiveTime(t.key);
-                    Bitmap loadThumbnail = mThumbnailCache.getCheckLastActiveTime(t.key);
+                    Drawable cachedIcon = mApplicationIconCache.getCheckLastActiveTime(t.key);
+                    Bitmap cachedThumbnail = mThumbnailCache.getCheckLastActiveTime(t.key);
                     if (Console.Enabled) {
                         Console.log(Constants.Log.App.TaskDataLoader,
                                 "  [TaskResourceLoader|load]",
-                                t + " icon: " + loadIcon + " thumbnail: " + loadThumbnail +
-                                        " forceLoad: " + forceLoadTask);
+                                t + " icon: " + cachedIcon + " thumbnail: " + cachedThumbnail);
                     }
-                    // Load the application icon
-                    if (loadIcon == null || forceLoadTask) {
+                    // Load the application icon if it is stale or we haven't cached one yet
+                    if (cachedIcon == null) {
+                        Drawable icon = null;
                         ActivityInfo info = ssp.getActivityInfo(t.key.baseIntent.getComponent(),
                                 t.userId);
-                        Drawable icon = ssp.getActivityIcon(info, t.userId);
-                        if (!mCancelled) {
-                            if (icon != null) {
-                                if (Console.Enabled) {
-                                    Console.log(Constants.Log.App.TaskDataLoader,
-                                            "    [TaskResourceLoader|loadIcon]", icon);
-                                }
-                                loadIcon = icon;
-                                mApplicationIconCache.put(t.key, icon);
+                        if (info != null) {
+                            icon = ssp.getActivityIcon(info, t.userId);
+                            if (Console.Enabled) {
+                                Console.log(Constants.Log.App.TaskDataLoader,
+                                        "    [TaskResourceLoader|loadedIcon]", icon);
                             }
                         }
+                        // If we can't load the icon, then set the default application icon into the
+                        // cache.  This will remain until the task's last active time is updated.
+                        cachedIcon = icon != null ? icon : mDefaultApplicationIcon;
+                        mApplicationIconCache.put(t.key, cachedIcon);
                     }
-                    // Load the thumbnail
-                    if (loadThumbnail == null || forceLoadTask) {
+                    // Load the thumbnail if it is stale or we haven't cached one yet
+                    if (cachedThumbnail == null) {
                         Bitmap thumbnail = ssp.getTaskThumbnail(t.key.id);
-                        if (!mCancelled) {
-                            if (thumbnail != null) {
-                                if (Console.Enabled) {
-                                    Console.log(Constants.Log.App.TaskDataLoader,
-                                            "    [TaskResourceLoader|loadThumbnail]", thumbnail);
-                                }
-                                thumbnail.setHasAlpha(false);
-                                loadThumbnail = thumbnail;
-                            } else {
-                                loadThumbnail = mDefaultThumbnail;
-                                Console.logError(mContext,
-                                        "Failed to load task top thumbnail for: " +
-                                                t.key.baseIntent.getComponent().getPackageName());
+                        if (thumbnail != null) {
+                            thumbnail.setHasAlpha(false);
+                            if (Console.Enabled) {
+                                Console.log(Constants.Log.App.TaskDataLoader,
+                                        "    [TaskResourceLoader|loadedThumbnail]", thumbnail);
                             }
-                            // We put the default thumbnail in the cache anyways
-                            mThumbnailCache.put(t.key, loadThumbnail);
                         }
+                        // Even if we can't load the icon, we set the default thumbnail into the
+                        // cache.  This will remain until the task's last active time is updated.
+                        cachedThumbnail = thumbnail != null ? thumbnail : mDefaultThumbnail;
+                        mThumbnailCache.put(t.key, cachedThumbnail);
                     }
                     if (!mCancelled) {
                         // Notify that the task data has changed
-                        final Drawable newIcon = loadIcon;
-                        final Bitmap newThumbnail = loadThumbnail;
+                        final Drawable newIcon = cachedIcon;
+                        final Bitmap newThumbnail = cachedThumbnail;
                         mMainThreadHandler.post(new Runnable() {
                             @Override
                             public void run() {
@@ -306,11 +280,11 @@
     /** Private Constructor */
     private RecentsTaskLoader(Context context) {
         // Calculate the cache sizes, we just use a reasonable number here similar to those
-        // suggested in the Android docs, 1/8th for the thumbnail cache and 1/32 of the max memory
+        // suggested in the Android docs, 1/6th for the thumbnail cache and 1/30 of the max memory
         // for icons.
-        int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
-        mMaxThumbnailCacheSize = maxMemory / 8;
-        mMaxIconCacheSize = mMaxThumbnailCacheSize / 4;
+        int maxMemory = (int) Runtime.getRuntime().maxMemory();
+        mMaxThumbnailCacheSize = maxMemory / 6;
+        mMaxIconCacheSize = mMaxThumbnailCacheSize / 5;
         int iconCacheSize = Constants.DebugFlags.App.DisableBackgroundCache ? 1 :
                 mMaxIconCacheSize;
         int thumbnailCacheSize = Constants.DebugFlags.App.DisableBackgroundCache ? 1 :
@@ -340,7 +314,7 @@
         mApplicationIconCache = new DrawableLruCache(iconCacheSize);
         mThumbnailCache = new BitmapLruCache(thumbnailCacheSize);
         mLoader = new TaskResourceLoader(mLoadQueue, mApplicationIconCache, mThumbnailCache,
-                mDefaultThumbnail);
+                mDefaultThumbnail, mDefaultApplicationIcon);
 
         if (Console.Enabled) {
             Console.log(Constants.Log.App.TaskDataLoader,
@@ -394,7 +368,7 @@
         }
         RecentsConfiguration config = RecentsConfiguration.getInstance();
         Resources res = context.getResources();
-        ArrayList<Task> tasksToForceLoad = new ArrayList<Task>();
+        LinkedHashSet<Task> tasksToLoad = new LinkedHashSet<Task>();
         TaskStack stack = new TaskStack();
         SpaceNode root = new SpaceNode();
         root.setStack(stack);
@@ -446,15 +420,16 @@
                     if (isForemostTask) {
                         // We force loading the application icon for the foremost task
                         task.applicationIcon = ssp.getActivityIcon(info, task.userId);
-                        if (task.applicationIcon != null) {
-                            mApplicationIconCache.put(task.key, task.applicationIcon);
-                        } else {
+                        if (task.applicationIcon == null) {
                             task.applicationIcon = mDefaultApplicationIcon;
                         }
+                        // Even if we can't load the icon we set the default application icon into
+                        // the cache.  This will remain until the task's last active time is updated.
+                        mApplicationIconCache.put(task.key, task.applicationIcon);
                     } else {
-                        // Either the task has updated, or we haven't cached any information for the
-                        // task, so reload it
-                        tasksToForceLoad.add(task);
+                        // Either the task has changed since the last active time, or it was not
+                        // previously cached, so try and load the task anew.
+                        tasksToLoad.add(task);
                     }
                 }
 
@@ -473,11 +448,13 @@
                         } else {
                             task.thumbnail = mDefaultThumbnail;
                         }
+                        // Even if we can't load the thumbnail we set the default thumbnail into
+                        // the cache.  This will remain until the task's last active time is updated.
                         mThumbnailCache.put(task.key, task.thumbnail);
                     } else {
-                        // Either the task has updated, or we haven't cached any information for the
-                        // task, so reload it
-                        tasksToForceLoad.add(task);
+                        // Either the task has changed since the last active time, or it was not
+                        // previously cached, so try and load the task anew.
+                        tasksToLoad.add(task);
                     }
                 }
             }
@@ -496,14 +473,14 @@
         }
 
         // Simulate the groupings that we describe
-        stack.createSimulatedAffiliatedGroupings();
+        stack.createAffiliatedGroupings();
 
         // Start the task loader
         mLoader.start(context);
 
-        // Add all the tasks that we are force/re-loading
-        for (Task t : tasksToForceLoad) {
-            mLoadQueue.addTask(t, true);
+        // Add all the tasks that we are reloading
+        for (Task t : tasksToLoad) {
+            mLoadQueue.addTask(t);
         }
 
         // Update the package monitor with the list of packages to listen for
@@ -526,7 +503,7 @@
             stack.addTask(new Task(t.persistentId, true, t.baseIntent, t.affiliatedTaskId, null,
                     null, 0, 0, t.firstActiveTime, t.lastActiveTime, (i == (taskCount - 1))));
         }
-        stack.createSimulatedAffiliatedGroupings();
+        stack.createAffiliatedGroupings();
         return stack;
     }
 
@@ -551,7 +528,7 @@
             requiresLoad = true;
         }
         if (requiresLoad) {
-            mLoadQueue.addTask(t, false);
+            mLoadQueue.addTask(t);
         }
         t.notifyTaskDataLoaded(thumbnail, applicationIcon);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 88e9f40..1670735 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -18,6 +18,7 @@
 
 import android.content.Intent;
 import android.graphics.Bitmap;
+import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import com.android.systemui.recents.misc.Utilities;
 
@@ -84,7 +85,7 @@
     public Drawable activityIcon;
     public String activityLabel;
     public int colorPrimary;
-    public int colorPrimaryGreyscale;
+    public boolean useLightOnPrimaryColor;
     public Bitmap thumbnail;
     public boolean isActive;
     public boolean canLockToTask;
@@ -104,7 +105,8 @@
         this.activityLabel = activityTitle;
         this.activityIcon = activityIcon;
         this.colorPrimary = colorPrimary;
-        this.colorPrimaryGreyscale = Utilities.colorToGreyscale(colorPrimary);
+        this.useLightOnPrimaryColor = Utilities.computeContrastBetweenColors(colorPrimary,
+                Color.WHITE) > 3f;
         this.isActive = isActive;
         this.canLockToTask = canLockToTask;
         this.userId = userId;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 7dd15a6..e3bcff0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -320,7 +320,7 @@
     /**
      * Temporary: This method will simulate affiliation groups by
      */
-    public void createSimulatedAffiliatedGroupings() {
+    public void createAffiliatedGroupings() {
         if (Constants.DebugFlags.App.EnableSimulatedTaskGroups) {
             HashMap<Task.TaskKey, Task> taskMap = new HashMap<Task.TaskKey, Task>();
             // Sort all tasks by increasing firstActiveTime of the task
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 99b012e..73bbf86 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -191,10 +191,6 @@
 
     /** Requests all task stacks to start their enter-recents animation */
     public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) {
-        // Handle the case when there are no views by incrementing and decrementing after all
-        // animations are started.
-        ctx.postAnimationTrigger.increment();
-
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
             View child = getChildAt(i);
@@ -203,18 +199,10 @@
                 stackView.startEnterRecentsAnimation(ctx);
             }
         }
-
-        // Handle the case when there are no views by incrementing and decrementing after all
-        // animations are started.
-        ctx.postAnimationTrigger.decrement();
     }
 
     /** Requests all task stacks to start their exit-recents animation */
     public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
-        // Handle the case when there are no views by incrementing and decrementing after all
-        // animations are started.
-        ctx.postAnimationTrigger.increment();
-
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
             View child = getChildAt(i);
@@ -224,10 +212,6 @@
             }
         }
 
-        // Handle the case when there are no views by incrementing and decrementing after all
-        // animations are started.
-        ctx.postAnimationTrigger.decrement();
-
         // Notify of the exit animation
         mCb.onExitToHomeAnimationTriggered();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
index db84962..deb9df3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
@@ -162,11 +162,10 @@
         }
         // Try and apply the system ui tint
         setBackgroundColor(t.colorPrimary);
-        mActivityDescription.setTextColor(Utilities.getIdealColorForBackgroundColorGreyscale(
-                t.colorPrimaryGreyscale, mConfig.taskBarViewLightTextColor,
-                mConfig.taskBarViewDarkTextColor));
-        mDismissButton.setImageDrawable(Utilities.getIdealResourceForBackgroundColorGreyscale(
-                t.colorPrimaryGreyscale, mLightDismissDrawable, mDarkDismissDrawable));
+        mActivityDescription.setTextColor(t.useLightOnPrimaryColor ?
+                mConfig.taskBarViewLightTextColor : mConfig.taskBarViewDarkTextColor);
+        mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ?
+                mLightDismissDrawable : mDarkDismissDrawable);
     }
 
     /** Unbinds the bar view from the task */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 599c590..adc808a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -302,7 +302,6 @@
             int[] visibleRange = mTmpVisibleRange;
             updateStackTransforms(mCurrentTaskTransforms, tasks, stackScroll, visibleRange, false);
             TaskViewTransform tmpTransform = new TaskViewTransform();
-            TaskStack.GroupTaskIndex gti = new TaskStack.GroupTaskIndex();
 
             // Return all the invisible children to the pool
             HashMap<Task, TaskView> taskChildViewMap = getTaskChildViewMap();
@@ -355,6 +354,47 @@
         }
     }
 
+    /** Updates the clip for each of the task views. */
+    void clipTaskViews() {
+        // Update the clip on each task child
+        if (Constants.DebugFlags.App.EnableTaskStackClipping) {
+            int childCount = getChildCount();
+            for (int i = 0; i < childCount - 1; i++) {
+                TaskView tv = (TaskView) getChildAt(i);
+                TaskView nextTv = null;
+                TaskView tmpTv = null;
+                int clipBottom = 0;
+                if (tv.shouldClipViewInStack()) {
+                    // Find the next view to clip against
+                    int nextIndex = i;
+                    while (nextIndex < getChildCount()) {
+                        tmpTv = (TaskView) getChildAt(++nextIndex);
+                        if (tmpTv != null && tmpTv.shouldClipViewInStack()) {
+                            nextTv = tmpTv;
+                            break;
+                        }
+                    }
+
+                    // Clip against the next view, this is just an approximation since we are
+                    // stacked and we can make assumptions about the visibility of the this
+                    // task relative to the ones in front of it.
+                    if (nextTv != null) {
+                        // XXX: Can hash the visible rects for this run
+                        tv.getHitRect(mTmpRect);
+                        nextTv.getHitRect(mTmpRect2);
+                        clipBottom = (mTmpRect.bottom - mTmpRect2.top);
+                    }
+                }
+                tv.setClipFromBottom(clipBottom);
+            }
+        }
+        if (getChildCount() > 0) {
+            // The front most task should never be clipped
+            TaskView tv = (TaskView) getChildAt(getChildCount() - 1);
+            tv.setClipFromBottom(0);
+        }
+    }
+
     /** Sets the current stack scroll */
     public void setStackScroll(int value) {
         mStackScroll = value;
@@ -641,50 +681,10 @@
                     Console.AnsiPurple);
         }
         synchronizeStackViewsWithModel();
+        clipTaskViews();
         super.dispatchDraw(canvas);
     }
 
-    @Override
-    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
-        if (Constants.DebugFlags.App.EnableTaskStackClipping) {
-            TaskView tv = (TaskView) child;
-            TaskView nextTv = null;
-            TaskView tmpTv = null;
-            if (tv.shouldClipViewInStack()) {
-                int curIndex = indexOfChild(tv);
-
-                // Find the next view to clip against
-                while (nextTv == null && curIndex < getChildCount()) {
-                    tmpTv = (TaskView) getChildAt(++curIndex);
-                    if (tmpTv != null && tmpTv.shouldClipViewInStack()) {
-                        nextTv = tmpTv;
-                    }
-                }
-
-                // Clip against the next view (if we aren't animating its alpha)
-                if (nextTv != null) {
-                    Rect curRect = tv.getClippingRect(mTmpRect);
-                    Rect nextRect = nextTv.getClippingRect(mTmpRect2);
-                    // The hit rects are relative to the task view, which needs to be offset by
-                    // the system bar height
-                    curRect.offset(0, mConfig.systemInsets.top);
-                    nextRect.offset(0, mConfig.systemInsets.top);
-                    // Compute the clip region
-                    Region clipRegion = new Region();
-                    clipRegion.op(curRect, Region.Op.UNION);
-                    clipRegion.op(nextRect, Region.Op.DIFFERENCE);
-                    // Clip the canvas
-                    int saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
-                    canvas.clipRegion(clipRegion);
-                    boolean invalidate = super.drawChild(canvas, child, drawingTime);
-                    canvas.restoreToCount(saveCount);
-                    return invalidate;
-                }
-            }
-        }
-        return super.drawChild(canvas, child, drawingTime);
-    }
-
     /** Computes the stack and task rects */
     public void computeRects(int width, int height, int insetLeft, int insetBottom) {
         // Compute the rects in the stack algorithm
@@ -1155,6 +1155,11 @@
         mStack.removeTask(task);
     }
 
+    @Override
+    public void onTaskViewClipStateChanged(TaskView tv) {
+        invalidate(mStackAlgorithm.mStackRect);
+    }
+
     /**** RecentsPackageMonitor.PackageCallbacks Implementation ****/
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
index 908e063..9c48896 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
@@ -135,9 +135,9 @@
         // Set the y translation
         if (boundedT < 0f) {
             transformOut.translationY = (int) ((Math.max(-numPeekCards, boundedT) /
-                    numPeekCards) * peekHeight - scaleYOffset - scaleBarYOffset);
+                    numPeekCards) * peekHeight - scaleYOffset);
         } else {
-            transformOut.translationY = (int) (boundedT * overlapHeight - scaleYOffset - scaleBarYOffset);
+            transformOut.translationY = (int) (boundedT * overlapHeight - scaleYOffset);
         }
 
         // Set the z translation
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 5524e15..ab14863 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -46,8 +46,9 @@
     interface TaskViewCallbacks {
         public void onTaskViewAppIconClicked(TaskView tv);
         public void onTaskViewAppInfoClicked(TaskView tv);
-        public void onTaskViewClicked(TaskView tv, Task t, boolean lockToTask);
+        public void onTaskViewClicked(TaskView tv, Task task, boolean lockToTask);
         public void onTaskViewDismissed(TaskView tv);
+        public void onTaskViewClipStateChanged(TaskView tv);
     }
 
     RecentsConfiguration mConfig;
@@ -65,7 +66,7 @@
     boolean mIsFocused;
     boolean mIsStub;
     boolean mClipViewInStack;
-    Rect mTmpRect = new Rect();
+    int mClipFromBottom;
     Paint mLayerPaint = new Paint();
 
     TaskThumbnailView mThumbnailView;
@@ -118,7 +119,9 @@
         setOutlineProvider(new ViewOutlineProvider() {
             @Override
             public boolean getOutline(View view, Outline outline) {
-                int height = getHeight() - mMaxFooterHeight + mFooterHeight;
+                // The current height is measured with the footer, so account for the footer height
+                // and the current clip (in the stack)
+                int height = getMeasuredHeight() - mClipFromBottom - mMaxFooterHeight + mFooterHeight;
                 outline.setRoundRect(0, 0, getWidth(), height,
                         mConfig.taskViewRoundedCornerRadiusPx);
                 return true;
@@ -483,15 +486,6 @@
         mBarView.setNoUserInteractionState();
     }
 
-    /** Returns the rect we want to clip (it may not be the full rect) */
-    Rect getClippingRect(Rect outRect) {
-        getHitRect(outRect);
-        // XXX: We should get the hit rect of the thumbnail view and intersect, but this is faster
-        outRect.right = outRect.left + mThumbnailView.getRight();
-        outRect.bottom = outRect.top + mThumbnailView.getBottom();
-        return outRect;
-    }
-
     /** Enable the hw layers on this task view */
     void enableHwLayers() {
         mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
@@ -506,7 +500,7 @@
         mLockToAppButtonView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
     }
 
-    /** Sets the stubbed state of this task view. */
+    /** Sets the stubbed state of this task view.
     void setStubState(boolean isStub) {
         if (!mIsStub && isStub) {
             // This is now a stub task view, so clip to the bar height, hide the thumbnail
@@ -519,7 +513,7 @@
             mThumbnailView.setVisibility(View.VISIBLE);
         }
         mIsStub = isStub;
-    }
+    } */
 
     /**
      * Returns whether this view should be clipped, or any views below should clip against this
@@ -533,19 +527,26 @@
     void setClipViewInStack(boolean clip) {
         if (clip != mClipViewInStack) {
             mClipViewInStack = clip;
-            if (getParent() instanceof View) {
-                getHitRect(mTmpRect);
-                ((View) getParent()).invalidate(mTmpRect);
-            }
+            mCb.onTaskViewClipStateChanged(this);
+        }
+    }
+
+    void setClipFromBottom(int clipFromBottom) {
+        clipFromBottom = Math.max(0, Math.min(getMeasuredHeight(), clipFromBottom));
+        if (mClipFromBottom != clipFromBottom) {
+            mClipFromBottom = clipFromBottom;
+            invalidateOutline();
         }
     }
 
     /** Sets the footer height. */
-    public void setFooterHeight(int height) {
-        mFooterHeight = height;
-        invalidateOutline();
-        invalidate(0, getMeasuredHeight() - mMaxFooterHeight, getMeasuredWidth(),
-                getMeasuredHeight());
+    public void setFooterHeight(int footerHeight) {
+        if (footerHeight != mFooterHeight) {
+            mFooterHeight = footerHeight;
+            invalidateOutline();
+            invalidate(0, getMeasuredHeight() - mMaxFooterHeight, getMeasuredWidth(),
+                    getMeasuredHeight());
+        }
     }
 
     /** Gets the footer height. */
@@ -677,6 +678,7 @@
         mTask = t;
         mTask.setCallbacks(this);
         if (getMeasuredWidth() == 0) {
+            // If we haven't yet measured, we should just set the footer height with any animation
             animateFooterVisibility(t.canLockToTask, 0, 0);
         } else {
             animateFooterVisibility(t.canLockToTask, mConfig.taskViewLockToAppLongAnimDuration, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
index ad2cf75..0c5d2a5 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
@@ -908,6 +908,9 @@
 
             if (mDialog != null) {
                 mDialog.show();
+                if (mCallback != null) {
+                    mCallback.onVisible(true);
+                }
             }
         }
 
@@ -1160,6 +1163,9 @@
                         mDialog.dismiss();
                         clearRemoteStreamController();
                         mActiveStreamType = -1;
+                        if (mCallback != null) {
+                            mCallback.onVisible(false);
+                        }
                     }
                 }
                 synchronized (sConfirmSafeVolumeLock) {
@@ -1262,5 +1268,6 @@
     public interface Callback {
         void onZenSettings();
         void onInteraction();
+        void onVisible(boolean visible);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index e4f5870..375f94c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -99,6 +99,13 @@
                     kvm.userActivity();
                 }
             }
+
+            @Override
+            public void onVisible(boolean visible) {
+                if (mAudioManager != null && mVolumeController != null) {
+                    mAudioManager.notifyVolumeControllerVisible(mVolumeController, visible);
+                }
+            }
         });
         mDialogPanel = mPanel;
     }
diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java
index 8222155..b3419c1 100644
--- a/services/core/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/core/java/com/android/server/location/GpsLocationProvider.java
@@ -62,6 +62,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.WorkSource;
 import android.provider.Settings;
@@ -70,6 +71,7 @@
 import android.telephony.SmsMessage;
 import android.telephony.TelephonyManager;
 import android.telephony.gsm.GsmCellLocation;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.NtpTrustedTime;
 
@@ -85,6 +87,8 @@
 import java.util.Map.Entry;
 import java.util.Properties;
 
+import libcore.io.IoUtils;
+
 /**
  * A GPS implementation of LocationProvider used by LocationManager.
  *
@@ -201,7 +205,9 @@
     private static final int AGPS_SETID_TYPE_IMSI = 1;
     private static final int AGPS_SETID_TYPE_MSISDN = 2;
 
-    private static final String PROPERTIES_FILE = "/etc/gps.conf";
+    private static final String PROPERTIES_FILE_PREFIX = "/etc/gps";
+    private static final String PROPERTIES_FILE_SUFFIX = ".conf";
+    private static final String DEFAULT_PROPERTIES_FILE = PROPERTIES_FILE_PREFIX + PROPERTIES_FILE_SUFFIX;
 
     private static final int GPS_GEOFENCE_UNAVAILABLE = 1<<0L;
     private static final int GPS_GEOFENCE_AVAILABLE = 1<<1L;
@@ -441,6 +447,44 @@
         return native_is_supported();
     }
 
+    private boolean loadPropertiesFile(String filename) {
+        mProperties = new Properties();
+        try {
+            File file = new File(filename);
+            FileInputStream stream = null;
+            try {
+                stream = new FileInputStream(file);
+                mProperties.load(stream);
+            } finally {
+                IoUtils.closeQuietly(stream);
+            }
+
+            mSuplServerHost = mProperties.getProperty("SUPL_HOST");
+            String portString = mProperties.getProperty("SUPL_PORT");
+            if (mSuplServerHost != null && portString != null) {
+                try {
+                    mSuplServerPort = Integer.parseInt(portString);
+                } catch (NumberFormatException e) {
+                    Log.e(TAG, "unable to parse SUPL_PORT: " + portString);
+                }
+            }
+
+            mC2KServerHost = mProperties.getProperty("C2K_HOST");
+            portString = mProperties.getProperty("C2K_PORT");
+            if (mC2KServerHost != null && portString != null) {
+                try {
+                    mC2KServerPort = Integer.parseInt(portString);
+                } catch (NumberFormatException e) {
+                    Log.e(TAG, "unable to parse C2K_PORT: " + portString);
+                }
+            }
+        } catch (IOException e) {
+            Log.w(TAG, "Could not open GPS configuration file " + filename);
+            return false;
+        }
+        return true;
+    }
+
     public GpsLocationProvider(Context context, ILocationManager ilocationManager,
             Looper looper) {
         mContext = context;
@@ -469,34 +513,15 @@
         mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
                 BatteryStats.SERVICE_NAME));
 
-        mProperties = new Properties();
-        try {
-            File file = new File(PROPERTIES_FILE);
-            FileInputStream stream = new FileInputStream(file);
-            mProperties.load(stream);
-            stream.close();
+        boolean propertiesLoaded = false;
+        final String gpsHardware = SystemProperties.get("ro.hardware.gps");
+        if (!TextUtils.isEmpty(gpsHardware)) {
+            final String propFilename = PROPERTIES_FILE_PREFIX + "." + gpsHardware + PROPERTIES_FILE_SUFFIX;
+            propertiesLoaded = loadPropertiesFile(propFilename);
+        }
 
-            mSuplServerHost = mProperties.getProperty("SUPL_HOST");
-            String portString = mProperties.getProperty("SUPL_PORT");
-            if (mSuplServerHost != null && portString != null) {
-                try {
-                    mSuplServerPort = Integer.parseInt(portString);
-                } catch (NumberFormatException e) {
-                    Log.e(TAG, "unable to parse SUPL_PORT: " + portString);
-                }
-            }
-
-            mC2KServerHost = mProperties.getProperty("C2K_HOST");
-            portString = mProperties.getProperty("C2K_PORT");
-            if (mC2KServerHost != null && portString != null) {
-                try {
-                    mC2KServerPort = Integer.parseInt(portString);
-                } catch (NumberFormatException e) {
-                    Log.e(TAG, "unable to parse C2K_PORT: " + portString);
-                }
-            }
-        } catch (IOException e) {
-            Log.w(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE);
+        if (!propertiesLoaded) {
+            loadPropertiesFile(DEFAULT_PROPERTIES_FILE);
         }
 
         // construct handler, listen for events
diff --git a/services/core/java/com/android/server/pm/KeySetHandle.java b/services/core/java/com/android/server/pm/KeySetHandle.java
new file mode 100644
index 0000000..640feb3
--- /dev/null
+++ b/services/core/java/com/android/server/pm/KeySetHandle.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+    * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+     *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.os.Binder;
+
+public class KeySetHandle extends Binder {
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java
index c19951f..37bedf3 100644
--- a/services/core/java/com/android/server/pm/KeySetManagerService.java
+++ b/services/core/java/com/android/server/pm/KeySetManagerService.java
@@ -16,7 +16,6 @@
 
 package com.android.server.pm;
 
-import android.content.pm.KeySet;
 import android.content.pm.PackageParser;
 import android.os.Binder;
 import android.util.ArraySet;
@@ -52,7 +51,7 @@
     /** Sentinel value returned when public key is not found. */
     protected static final long PUBLIC_KEY_NOT_FOUND = -1;
 
-    private final LongSparseArray<KeySet> mKeySets;
+    private final LongSparseArray<KeySetHandle> mKeySets;
 
     private final LongSparseArray<PublicKey> mPublicKeys;
 
@@ -65,7 +64,7 @@
     private static long lastIssuedKeyId = 0;
 
     public KeySetManagerService(Map<String, PackageSetting> packages) {
-        mKeySets = new LongSparseArray<KeySet>();
+        mKeySets = new LongSparseArray<KeySetHandle>();
         mPublicKeys = new LongSparseArray<PublicKey>();
         mKeySetMapping = new LongSparseArray<ArraySet<Long>>();
         mPackages = packages;
@@ -82,7 +81,7 @@
      *
      * Note that this can return true for multiple KeySets.
      */
-    public boolean packageIsSignedByLPr(String packageName, KeySet ks) {
+    public boolean packageIsSignedByLPr(String packageName, KeySetHandle ks) {
         PackageSetting pkg = mPackages.get(packageName);
         if (pkg == null) {
             throw new NullPointerException("Invalid package name");
@@ -91,16 +90,42 @@
             throw new NullPointerException("Package has no KeySet data");
         }
         long id = getIdByKeySetLPr(ks);
+        if (id == KEYSET_NOT_FOUND) {
+                return false;
+        }
         return pkg.keySetData.packageIsSignedBy(id);
     }
 
     /**
+     * Determine if a package is signed by the given KeySet.
+     *
+     * Returns false if the package was not signed by all the
+     * keys in the KeySet, or if the package was signed by keys
+     * not in the KeySet.
+     *
+     * Note that this can return only for one KeySet.
+     */
+    public boolean packageIsSignedByExactlyLPr(String packageName, KeySetHandle ks) {
+        PackageSetting pkg = mPackages.get(packageName);
+        if (pkg == null) {
+            throw new NullPointerException("Invalid package name");
+        }
+        if (pkg.keySetData == null
+            || pkg.keySetData.getProperSigningKeySet()
+            == PackageKeySetData.KEYSET_UNASSIGNED) {
+            throw new NullPointerException("Package has no KeySet data");
+        }
+        long id = getIdByKeySetLPr(ks);
+        return pkg.keySetData.getProperSigningKeySet() == id;
+    }
+
+    /**
      * This informs the system that the given package has defined a KeySet
      * in its manifest that a) contains the given keys and b) is named
      * alias by that package.
      */
     public void addDefinedKeySetToPackageLPw(String packageName,
-            Set<PublicKey> keys, String alias) {
+            ArraySet<PublicKey> keys, String alias) {
         if ((packageName == null) || (keys == null) || (alias == null)) {
             Slog.w(TAG, "Got null argument for a defined keyset, ignoring!");
             return;
@@ -110,7 +135,7 @@
             throw new NullPointerException("Unknown package");
         }
         // Add to KeySets, then to package
-        KeySet ks = addKeySetLPw(keys);
+        KeySetHandle ks = addKeySetLPw(keys);
         long id = getIdByKeySetLPr(ks);
         pkg.keySetData.addDefinedKeySet(id, alias);
     }
@@ -137,19 +162,18 @@
      * was signed by the provided KeySet.
      */
     public void addSigningKeySetToPackageLPw(String packageName,
-            Set<PublicKey> signingKeys) {
+            ArraySet<PublicKey> signingKeys) {
         if ((packageName == null) || (signingKeys == null)) {
             Slog.w(TAG, "Got null argument for a signing keyset, ignoring!");
             return;
         }
         // add the signing KeySet
-        KeySet ks = addKeySetLPw(signingKeys);
+        KeySetHandle ks = addKeySetLPw(signingKeys);
         long id = getIdByKeySetLPr(ks);
-        Set<Long> publicKeyIds = mKeySetMapping.get(id);
+        ArraySet<Long> publicKeyIds = mKeySetMapping.get(id);
         if (publicKeyIds == null) {
             throw new NullPointerException("Got invalid KeySet id");
         }
-
         // attach it to the package
         PackageSetting pkg = mPackages.get(packageName);
         if (pkg == null) {
@@ -160,7 +184,7 @@
         // KeySet id to the package's signing KeySets
         for (int keySetIndex = 0; keySetIndex < mKeySets.size(); keySetIndex++) {
             long keySetID = mKeySets.keyAt(keySetIndex);
-            Set<Long> definedKeys = mKeySetMapping.get(keySetID);
+            ArraySet<Long> definedKeys = mKeySetMapping.get(keySetID);
             if (publicKeyIds.containsAll(definedKeys)) {
                 pkg.keySetData.addSigningKeySet(keySetID);
             }
@@ -171,9 +195,9 @@
      * Fetches the stable identifier associated with the given KeySet. Returns
      * {@link #KEYSET_NOT_FOUND} if the KeySet... wasn't found.
      */
-    private long getIdByKeySetLPr(KeySet ks) {
+    private long getIdByKeySetLPr(KeySetHandle ks) {
         for (int keySetIndex = 0; keySetIndex < mKeySets.size(); keySetIndex++) {
-            KeySet value = mKeySets.valueAt(keySetIndex);
+            KeySetHandle value = mKeySets.valueAt(keySetIndex);
             if (ks.equals(value)) {
                 return mKeySets.keyAt(keySetIndex);
             }
@@ -187,25 +211,24 @@
      * Returns {@link #KEYSET_NOT_FOUND} if the identifier doesn't
      * identify a {@link KeySet}.
      */
-    public KeySet getKeySetByIdLPr(long id) {
+    public KeySetHandle getKeySetByIdLPr(long id) {
         return mKeySets.get(id);
     }
 
     /**
-     * Fetches the {@link KeySet} that a given package refers to by the provided alias.
-     *
-     * @throws IllegalArgumentException if the package has no keyset data.
-     * @throws NullPointerException if the package is unknown.
+     * Fetches the {@link KeySetHandle} that a given package refers to by the
+     * provided alias. Returns null if the package is unknown or does not have a
+     * KeySet corresponding to that alias.
      */
-    public KeySet getKeySetByAliasAndPackageNameLPr(String packageName, String alias) {
+    public KeySetHandle getKeySetByAliasAndPackageNameLPr(String packageName, String alias) {
         PackageSetting p = mPackages.get(packageName);
-        if (p == null) {
-            throw new NullPointerException("Unknown package");
+        if (p == null || p.keySetData == null) {
+                return null;
         }
-        if (p.keySetData == null) {
-            throw new IllegalArgumentException("Package has no keySet data");
+        Long keySetId = p.keySetData.getAliases().get(alias);
+        if (keySetId == null) {
+            throw new IllegalArgumentException("Unknown KeySet alias: " + alias);
         }
-        long keySetId = p.keySetData.getAliases().get(alias);
         return mKeySets.get(keySetId);
     }
 
@@ -214,7 +237,7 @@
      * KeySet id.
      *
      * Returns {@code null} if the identifier doesn't
-     * identify a {@link KeySet}.
+     * identify a {@link KeySetHandle}.
      */
     public ArraySet<PublicKey> getPublicKeysFromKeySetLPr(long id) {
         if(mKeySetMapping.get(id) == null) {
@@ -228,36 +251,32 @@
     }
 
     /**
-     * Fetches all the known {@link KeySet KeySets} that signed the given
+     * Fetches the proper {@link KeySetHandle KeySet} that signed the given
      * package.
      *
      * @throws IllegalArgumentException if the package has no keyset data.
      * @throws NullPointerException if the package is unknown.
      */
-    public Set<KeySet> getSigningKeySetsByPackageNameLPr(String packageName) {
-        Set<KeySet> signingKeySets = new ArraySet<KeySet>();
+    public KeySetHandle  getSigningKeySetByPackageNameLPr(String packageName) {
         PackageSetting p = mPackages.get(packageName);
-        if (p == null) {
-            throw new NullPointerException("Unknown package");
+        if (p == null
+            || p.keySetData == null
+            || p.keySetData.getProperSigningKeySet()
+            == PackageKeySetData.KEYSET_UNASSIGNED) {
+            return null;
         }
-        if (p.keySetData == null || p.keySetData.getSigningKeySets() == null) {
-            throw new IllegalArgumentException("Package has no keySet data");
-        }
-        for (long l : p.keySetData.getSigningKeySets()) {
-            signingKeySets.add(mKeySets.get(l));
-        }
-        return signingKeySets;
+        return mKeySets.get(p.keySetData.getProperSigningKeySet());
     }
 
     /**
-     * Fetches all the known {@link KeySet KeySets} that may upgrade the given
+     * Fetches all the known {@link KeySetHandle KeySets} that may upgrade the given
      * package.
      *
      * @throws IllegalArgumentException if the package has no keyset data.
      * @throws NullPointerException if the package is unknown.
      */
-    public ArraySet<KeySet> getUpgradeKeySetsByPackageNameLPr(String packageName) {
-        ArraySet<KeySet> upgradeKeySets = new ArraySet<KeySet>();
+    public ArraySet<KeySetHandle> getUpgradeKeySetsByPackageNameLPr(String packageName) {
+        ArraySet<KeySetHandle> upgradeKeySets = new ArraySet<KeySetHandle>();
         PackageSetting p = mPackages.get(packageName);
         if (p == null) {
             throw new NullPointerException("Unknown package");
@@ -287,7 +306,7 @@
      *
      * Throws if the provided set is {@code null}.
      */
-    private KeySet addKeySetLPw(Set<PublicKey> keys) {
+    private KeySetHandle addKeySetLPw(ArraySet<PublicKey> keys) {
         if (keys == null) {
             throw new NullPointerException("Provided keys cannot be null");
         }
@@ -305,7 +324,7 @@
         }
 
         // create the KeySet object
-        KeySet ks = new KeySet(new Binder());
+        KeySetHandle ks = new KeySetHandle();
         // get the first unoccupied slot in mKeySets
         long id = getFreeKeySetIDLPw();
         // add the KeySet object to it
@@ -318,7 +337,7 @@
             if (p.keySetData != null) {
                 long pProperSigning = p.keySetData.getProperSigningKeySet();
                 if (pProperSigning != PackageKeySetData.KEYSET_UNASSIGNED) {
-                    Set<Long> pSigningKeys = mKeySetMapping.get(pProperSigning);
+                    ArraySet<Long> pSigningKeys = mKeySetMapping.get(pProperSigning);
                     if (pSigningKeys.containsAll(addedKeyIds)) {
                         p.keySetData.addSigningKeySet(id);
                     }
@@ -353,7 +372,7 @@
      */
     private long getIdFromKeyIdsLPr(Set<Long> publicKeyIds) {
         for (int keyMapIndex = 0; keyMapIndex < mKeySetMapping.size(); keyMapIndex++) {
-            Set<Long> value = mKeySetMapping.valueAt(keyMapIndex);
+            ArraySet<Long> value = mKeySetMapping.valueAt(keyMapIndex);
             if (value.equals(publicKeyIds)) {
                 return mKeySetMapping.keyAt(keyMapIndex);
             }
@@ -582,7 +601,7 @@
         serializer.startTag(null, "keysets");
         for (int keySetIndex = 0; keySetIndex < mKeySetMapping.size(); keySetIndex++) {
             long id = mKeySetMapping.keyAt(keySetIndex);
-            Set<Long> keys = mKeySetMapping.valueAt(keySetIndex);
+            ArraySet<Long> keys = mKeySetMapping.valueAt(keySetIndex);
             serializer.startTag(null, "keyset");
             serializer.attribute(null, "identifier", Long.toString(id));
             for (long keyId : keys) {
@@ -662,7 +681,7 @@
             final String tagName = parser.getName();
             if (tagName.equals("keyset")) {
                 currentKeySetId = readIdentifierLPw(parser);
-                mKeySets.put(currentKeySetId, new KeySet(new Binder()));
+                mKeySets.put(currentKeySetId, new KeySetHandle());
                 mKeySetMapping.put(currentKeySetId, new ArraySet<Long>());
             } else if (tagName.equals("key-id")) {
                 long id = readIdentifierLPw(parser);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index c399fa2..41ab66a 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -177,7 +177,8 @@
     @Override
     public void setClientProgress(int progress) {
         mClientProgress = progress;
-        mProgress = MathUtils.constrain((mClientProgress * 8 * 100) / (params.progressMax * 10), 0, 80);
+        mProgress = MathUtils.constrain(
+                (int) (((float) mClientProgress) / ((float) params.progressMax)) * 80, 0, 80);
         mCallback.onSessionProgress(this, mProgress);
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 101ef92..cfba19c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -9331,14 +9331,18 @@
                 return false;
             } else {
                 final File beforeCodeFile = codeFile;
-                final File afterCodeFile = new File(mAppInstallDir,
-                        getNextCodePath(oldCodePath, pkg.packageName, null));
+                final File afterCodeFile = getNextCodePath(pkg.packageName);
 
                 Slog.d(TAG, "Renaming " + beforeCodeFile + " to " + afterCodeFile);
-                if (!beforeCodeFile.renameTo(afterCodeFile)) {
+                try {
+                    Os.rename(beforeCodeFile.getAbsolutePath(), afterCodeFile.getAbsolutePath());
+                } catch (ErrnoException e) {
+                    Slog.d(TAG, "Failed to rename", e);
                     return false;
                 }
+
                 if (!SELinux.restoreconRecursive(afterCodeFile)) {
+                    Slog.d(TAG, "Failed to restorecon");
                     return false;
                 }
 
@@ -9811,6 +9815,16 @@
         return prefix + idxStr;
     }
 
+    private File getNextCodePath(String packageName) {
+        int suffix = 1;
+        File result;
+        do {
+            result = new File(mAppInstallDir, packageName + "-" + suffix);
+            suffix++;
+        } while (result.exists());
+        return result;
+    }
+
     // Utility method used to ignore ADD/REMOVE events
     // by directory observer.
     private static boolean ignoreCodePath(String fullPathStr) {
@@ -13255,4 +13269,83 @@
         }
         return mUserNeedsBadging.valueAt(index);
     }
+
+    @Override
+    public KeySetHandle getKeySetByAlias(String packageName, String alias) {
+        if (packageName == null || alias == null) {
+            return null;
+        }
+        synchronized(mPackages) {
+            final PackageParser.Package pkg = mPackages.get(packageName);
+            if (pkg == null) {
+                Slog.w(TAG, "KeySet requested for unknown package:" + packageName);
+                throw new IllegalArgumentException("Unknown package: " + packageName);
+            }
+            if (pkg.applicationInfo.uid != Binder.getCallingUid()
+                    && Process.SYSTEM_UID != Binder.getCallingUid()) {
+                throw new SecurityException("May not access KeySets defined by"
+                        + " aliases in other applications.");
+            }
+            KeySetManagerService ksms = mSettings.mKeySetManagerService;
+            return ksms.getKeySetByAliasAndPackageNameLPr(packageName, alias);
+        }
+    }
+
+    @Override
+    public KeySetHandle getSigningKeySet(String packageName) {
+        if (packageName == null) {
+            return null;
+        }
+        synchronized(mPackages) {
+            final PackageParser.Package pkg = mPackages.get(packageName);
+            if (pkg == null) {
+                Slog.w(TAG, "KeySet requested for unknown package:" + packageName);
+                throw new IllegalArgumentException("Unknown package: " + packageName);
+            }
+            if (pkg.applicationInfo.uid != Binder.getCallingUid()
+                    && Process.SYSTEM_UID != Binder.getCallingUid()) {
+                throw new SecurityException("May not access signing KeySet of other apps.");
+            }
+            KeySetManagerService ksms = mSettings.mKeySetManagerService;
+            return ksms.getSigningKeySetByPackageNameLPr(packageName);
+        }
+    }
+
+    @Override
+    public boolean isPackageSignedByKeySet(String packageName, IBinder ks) {
+        if (packageName == null || ks == null) {
+            return false;
+        }
+        synchronized(mPackages) {
+            final PackageParser.Package pkg = mPackages.get(packageName);
+            if (pkg == null) {
+                Slog.w(TAG, "KeySet requested for unknown package:" + packageName);
+                throw new IllegalArgumentException("Unknown package: " + packageName);
+            }
+            if (ks instanceof KeySetHandle) {
+                KeySetManagerService ksms = mSettings.mKeySetManagerService;
+                return ksms.packageIsSignedByLPr(packageName, (KeySetHandle) ks);
+            }
+            return false;
+        }
+    }
+
+    @Override
+    public boolean isPackageSignedByKeySetExactly(String packageName, IBinder ks) {
+        if (packageName == null || ks == null) {
+            return false;
+        }
+        synchronized(mPackages) {
+            final PackageParser.Package pkg = mPackages.get(packageName);
+            if (pkg == null) {
+                Slog.w(TAG, "KeySet requested for unknown package:" + packageName);
+                throw new IllegalArgumentException("Unknown package: " + packageName);
+            }
+            if (ks instanceof KeySetHandle) {
+                KeySetManagerService ksms = mSettings.mKeySetManagerService;
+                return ksms.packageIsSignedByExactlyLPr(packageName, (KeySetHandle) ks);
+            }
+            return false;
+        }
+    }
 }
diff --git a/telecomm/java/android/telecomm/TelecommConstants.java b/telecomm/java/android/telecomm/TelecommConstants.java
index b9fb40c..b21ea60 100644
--- a/telecomm/java/android/telecomm/TelecommConstants.java
+++ b/telecomm/java/android/telecomm/TelecommConstants.java
@@ -51,6 +51,12 @@
     public static final String ACTION_CONNECTION_SERVICE = ConnectionService.class.getName();
 
     /**
+     * The {@link Intent} action used to configure a {@link ConnectionService}.
+     */
+    public static final String ACTION_CONNECTION_SERVICE_CONFIGURE =
+            "android.intent.action.CONNECTION_SERVICE_CONFIGURE";
+
+    /**
      * Optional extra for {@link Intent#ACTION_CALL} containing a boolean that determines whether
      * the speakerphone should be automatically turned on for an outgoing call.
      */
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index a14714a..648c418 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -31,6 +31,7 @@
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageStatsObserver;
 import android.content.pm.InstrumentationInfo;
+import android.content.pm.KeySet;
 import android.content.pm.ManifestDigest;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
@@ -616,6 +617,26 @@
         throw new UnsupportedOperationException();
     }
 
+    @Override
+    public KeySet getKeySetByAlias(String packageName, String alias) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public KeySet getSigningKeySet(String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isSignedBy(String packageName, KeySet ks) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isSignedByExactly(String packageName, KeySet ks) {
+        throw new UnsupportedOperationException();
+    }
+
     /**
      * @hide
      */