Merge "MidiManager: Improve output of "dumpsys midi"" into mnc-dev
diff --git a/api/current.txt b/api/current.txt
index 7df09a1..cdc2404 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -28506,6 +28506,53 @@
   public abstract class KeyStoreKeyProperties {
   }
 
+  public static abstract class KeyStoreKeyProperties.Algorithm {
+    field public static final java.lang.String AES = "AES";
+    field public static final java.lang.String EC = "EC";
+    field public static final java.lang.String HMAC_SHA1 = "HmacSHA1";
+    field public static final java.lang.String HMAC_SHA224 = "HmacSHA224";
+    field public static final java.lang.String HMAC_SHA256 = "HmacSHA256";
+    field public static final java.lang.String HMAC_SHA384 = "HmacSHA384";
+    field public static final java.lang.String HMAC_SHA512 = "HmacSHA512";
+    field public static final java.lang.String RSA = "RSA";
+  }
+
+  public static abstract class KeyStoreKeyProperties.AlgorithmEnum implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class KeyStoreKeyProperties.BlockMode {
+    field public static final java.lang.String CBC = "CBC";
+    field public static final java.lang.String CTR = "CTR";
+    field public static final java.lang.String ECB = "ECB";
+    field public static final java.lang.String GCM = "GCM";
+  }
+
+  public static abstract class KeyStoreKeyProperties.BlockModeEnum implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class KeyStoreKeyProperties.Digest {
+    field public static final java.lang.String MD5 = "MD5";
+    field public static final java.lang.String NONE = "NONE";
+    field public static final java.lang.String SHA1 = "SHA-1";
+    field public static final java.lang.String SHA224 = "SHA-224";
+    field public static final java.lang.String SHA256 = "SHA-256";
+    field public static final java.lang.String SHA384 = "SHA-384";
+    field public static final java.lang.String SHA512 = "SHA-512";
+  }
+
+  public static abstract class KeyStoreKeyProperties.DigestEnum implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class KeyStoreKeyProperties.EncryptionPadding {
+    field public static final java.lang.String NONE = "NoPadding";
+    field public static final java.lang.String PKCS7 = "PKCS7Padding";
+    field public static final java.lang.String RSA_OAEP = "OAEPPadding";
+    field public static final java.lang.String RSA_PKCS1 = "PKCS1Padding";
+  }
+
+  public static abstract class KeyStoreKeyProperties.EncryptionPaddingEnum implements java.lang.annotation.Annotation {
+  }
+
   public static abstract class KeyStoreKeyProperties.Origin {
     field public static final int GENERATED = 1; // 0x1
     field public static final int IMPORTED = 2; // 0x2
@@ -28525,6 +28572,14 @@
   public static abstract class KeyStoreKeyProperties.PurposeEnum implements java.lang.annotation.Annotation {
   }
 
+  public static abstract class KeyStoreKeyProperties.SignaturePadding {
+    field public static final java.lang.String RSA_PKCS1 = "PKCS1";
+    field public static final java.lang.String RSA_PSS = "PSS";
+  }
+
+  public static abstract class KeyStoreKeyProperties.SignaturePaddingEnum implements java.lang.annotation.Annotation {
+  }
+
   public class KeyStoreKeySpec implements java.security.spec.KeySpec {
     method public java.lang.String[] getBlockModes();
     method public java.lang.String[] getDigests();
diff --git a/api/system-current.txt b/api/system-current.txt
index 0662d93..7012773 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -30520,6 +30520,53 @@
   public abstract class KeyStoreKeyProperties {
   }
 
+  public static abstract class KeyStoreKeyProperties.Algorithm {
+    field public static final java.lang.String AES = "AES";
+    field public static final java.lang.String EC = "EC";
+    field public static final java.lang.String HMAC_SHA1 = "HmacSHA1";
+    field public static final java.lang.String HMAC_SHA224 = "HmacSHA224";
+    field public static final java.lang.String HMAC_SHA256 = "HmacSHA256";
+    field public static final java.lang.String HMAC_SHA384 = "HmacSHA384";
+    field public static final java.lang.String HMAC_SHA512 = "HmacSHA512";
+    field public static final java.lang.String RSA = "RSA";
+  }
+
+  public static abstract class KeyStoreKeyProperties.AlgorithmEnum implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class KeyStoreKeyProperties.BlockMode {
+    field public static final java.lang.String CBC = "CBC";
+    field public static final java.lang.String CTR = "CTR";
+    field public static final java.lang.String ECB = "ECB";
+    field public static final java.lang.String GCM = "GCM";
+  }
+
+  public static abstract class KeyStoreKeyProperties.BlockModeEnum implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class KeyStoreKeyProperties.Digest {
+    field public static final java.lang.String MD5 = "MD5";
+    field public static final java.lang.String NONE = "NONE";
+    field public static final java.lang.String SHA1 = "SHA-1";
+    field public static final java.lang.String SHA224 = "SHA-224";
+    field public static final java.lang.String SHA256 = "SHA-256";
+    field public static final java.lang.String SHA384 = "SHA-384";
+    field public static final java.lang.String SHA512 = "SHA-512";
+  }
+
+  public static abstract class KeyStoreKeyProperties.DigestEnum implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class KeyStoreKeyProperties.EncryptionPadding {
+    field public static final java.lang.String NONE = "NoPadding";
+    field public static final java.lang.String PKCS7 = "PKCS7Padding";
+    field public static final java.lang.String RSA_OAEP = "OAEPPadding";
+    field public static final java.lang.String RSA_PKCS1 = "PKCS1Padding";
+  }
+
+  public static abstract class KeyStoreKeyProperties.EncryptionPaddingEnum implements java.lang.annotation.Annotation {
+  }
+
   public static abstract class KeyStoreKeyProperties.Origin {
     field public static final int GENERATED = 1; // 0x1
     field public static final int IMPORTED = 2; // 0x2
@@ -30539,6 +30586,14 @@
   public static abstract class KeyStoreKeyProperties.PurposeEnum implements java.lang.annotation.Annotation {
   }
 
+  public static abstract class KeyStoreKeyProperties.SignaturePadding {
+    field public static final java.lang.String RSA_PKCS1 = "PKCS1";
+    field public static final java.lang.String RSA_PSS = "PSS";
+  }
+
+  public static abstract class KeyStoreKeyProperties.SignaturePaddingEnum implements java.lang.annotation.Annotation {
+  }
+
   public class KeyStoreKeySpec implements java.security.spec.KeySpec {
     method public java.lang.String[] getBlockModes();
     method public java.lang.String[] getDigests();
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index bb25ec6..21dc1e2 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -630,7 +630,10 @@
                     }
                     glDisable(GL_SCISSOR_TEST);
                 }
-                glDrawTexiOES(xc, yc, 0, animation.width, animation.height);
+                // specify the y center as ceiling((mHeight - animation.height) / 2)
+                // which is equivalent to mHeight - (yc + animation.height)
+                glDrawTexiOES(xc, mHeight - (yc + animation.height),
+                              0, animation.width, animation.height);
                 eglSwapBuffers(mDisplay, mSurface);
 
                 nsecs_t now = systemTime();
diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java
index 427ecce..435d5ab 100644
--- a/core/java/android/animation/AnimatorInflater.java
+++ b/core/java/android/animation/AnimatorInflater.java
@@ -108,7 +108,7 @@
             float pathErrorScale) throws NotFoundException {
         final ConfigurationBoundResourceCache<Animator> animatorCache = resources
                 .getAnimatorCache();
-        Animator animator = animatorCache.get(id, theme);
+        Animator animator = animatorCache.getInstance(id, theme);
         if (animator != null) {
             if (DBG_ANIMATOR_INFLATER) {
                 Log.d(TAG, "loaded animator from cache, " + resources.getResourceName(id));
@@ -157,7 +157,7 @@
         final ConfigurationBoundResourceCache<StateListAnimator> cache = resources
                 .getStateListAnimatorCache();
         final Theme theme = context.getTheme();
-        StateListAnimator animator = cache.get(id, theme);
+        StateListAnimator animator = cache.getInstance(id, theme);
         if (animator != null) {
             return animator;
         }
diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java
index 5790682..cdd72be 100644
--- a/core/java/android/animation/LayoutTransition.java
+++ b/core/java/android/animation/LayoutTransition.java
@@ -28,6 +28,7 @@
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * This class enables automatic animations on layout changes in ViewGroup objects. To enable
@@ -757,7 +758,7 @@
         // reset the inter-animation delay, in case we use it later
         staggerDelay = 0;
 
-        final ViewTreeObserver observer = parent.getViewTreeObserver(); // used for later cleanup
+        final ViewTreeObserver observer = parent.getViewTreeObserver();
         if (!observer.isAlive()) {
             // If the observer's not in a good state, skip the transition
             return;
@@ -790,21 +791,9 @@
         // This is the cleanup step. When we get this rendering event, we know that all of
         // the appropriate animations have been set up and run. Now we can clear out the
         // layout listeners.
-        observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
-            public boolean onPreDraw() {
-                parent.getViewTreeObserver().removeOnPreDrawListener(this);
-                int count = layoutChangeListenerMap.size();
-                if (count > 0) {
-                    Collection<View> views = layoutChangeListenerMap.keySet();
-                    for (View view : views) {
-                        View.OnLayoutChangeListener listener = layoutChangeListenerMap.get(view);
-                        view.removeOnLayoutChangeListener(listener);
-                    }
-                }
-                layoutChangeListenerMap.clear();
-                return true;
-            }
-        });
+        CleanupCallback callback = new CleanupCallback(layoutChangeListenerMap, parent);
+        observer.addOnPreDrawListener(callback);
+        parent.addOnAttachStateChangeListener(callback);
     }
 
     /**
@@ -1499,4 +1488,50 @@
                 View view, int transitionType);
     }
 
+    /**
+     * Utility class to clean up listeners after animations are setup. Cleanup happens
+     * when either the OnPreDrawListener method is called or when the parent is detached,
+     * whichever comes first.
+     */
+    private static final class CleanupCallback implements ViewTreeObserver.OnPreDrawListener,
+            View.OnAttachStateChangeListener {
+
+        final Map<View, View.OnLayoutChangeListener> layoutChangeListenerMap;
+        final ViewGroup parent;
+
+        CleanupCallback(Map<View, View.OnLayoutChangeListener> listenerMap, ViewGroup parent) {
+            this.layoutChangeListenerMap = listenerMap;
+            this.parent = parent;
+        }
+
+        private void cleanup() {
+            parent.getViewTreeObserver().removeOnPreDrawListener(this);
+            parent.removeOnAttachStateChangeListener(this);
+            int count = layoutChangeListenerMap.size();
+            if (count > 0) {
+                Collection<View> views = layoutChangeListenerMap.keySet();
+                for (View view : views) {
+                    View.OnLayoutChangeListener listener = layoutChangeListenerMap.get(view);
+                    view.removeOnLayoutChangeListener(listener);
+                }
+                layoutChangeListenerMap.clear();
+            }
+        }
+
+        @Override
+        public void onViewAttachedToWindow(View v) {
+        }
+
+        @Override
+        public void onViewDetachedFromWindow(View v) {
+            cleanup();
+        }
+
+        @Override
+        public boolean onPreDraw() {
+            cleanup();
+            return true;
+        }
+    };
+
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index da6d8c5..f16406a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -38,6 +38,7 @@
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.content.res.Resources.Theme;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteDebug;
 import android.database.sqlite.SQLiteDebug.DbStats;
@@ -4186,9 +4187,14 @@
             if (!mConfiguration.isOtherSeqNewer(config) && compat == null) {
                 return;
             }
-            configDiff = mConfiguration.diff(config);
-            mConfiguration.updateFrom(config);
+
+            configDiff = mConfiguration.updateFrom(config);
             config = applyCompatConfiguration(mCurDefaultDisplayDpi);
+
+            final Theme systemTheme = getSystemContext().getTheme();
+            if ((systemTheme.getChangingConfigurations() & configDiff) != 0) {
+                systemTheme.rebase();
+            }
         }
 
         ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(false, config);
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index 2939322..968c956 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -476,9 +476,8 @@
             tempRect.set(0, 0, width, height);
             view.getMatrix().mapRect(tempRect);
 
-            ViewGroup parent = (ViewGroup) view.getParent();
-            left = leftInParent - tempRect.left + parent.getScrollX();
-            top = topInParent - tempRect.top + parent.getScrollY();
+            left = leftInParent - tempRect.left;
+            top = topInParent - tempRect.top;
             right = left + width;
             bottom = top + height;
         }
@@ -506,7 +505,7 @@
             ViewGroup parent = (ViewGroup) view.getParent();
             Matrix matrix = new Matrix();
             parent.transformMatrixToLocal(matrix);
-
+            matrix.postTranslate(parent.getScrollX(), parent.getScrollY());
             mSharedElementParentMatrices.add(matrix);
         }
     }
@@ -521,6 +520,7 @@
                 // Find the location in the view's parent
                 ViewGroup parent = (ViewGroup) viewParent;
                 parent.transformMatrixToLocal(matrix);
+                matrix.postTranslate(parent.getScrollX(), parent.getScrollY());
             }
         } else {
             // The indices of mSharedElementParentMatrices matches the
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 525059f..8d96f5c2 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -785,6 +785,7 @@
     private native final void deleteTheme(long theme);
     /*package*/ native static final void applyThemeStyle(long theme, int styleRes, boolean force);
     /*package*/ native static final void copyTheme(long dest, long source);
+    /*package*/ native static final void clearTheme(long theme);
     /*package*/ native static final int loadThemeAttributeValue(long theme, int ident,
                                                                 TypedValue outValue,
                                                                 boolean resolve);
diff --git a/core/java/android/content/res/ConfigurationBoundResourceCache.java b/core/java/android/content/res/ConfigurationBoundResourceCache.java
index cde7e84..fecda87 100644
--- a/core/java/android/content/res/ConfigurationBoundResourceCache.java
+++ b/core/java/android/content/res/ConfigurationBoundResourceCache.java
@@ -1,138 +1,58 @@
 /*
-* Copyright (C) 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 android.content.res;
+ * Copyright (C) 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.
+ */
 
-import android.util.ArrayMap;
-import android.util.LongSparseArray;
-import java.lang.ref.WeakReference;
+package android.content.res;
 
 /**
  * A Cache class which can be used to cache resource objects that are easy to clone but more
  * expensive to inflate.
- * @hide
+ *
+ * @hide For internal use only.
  */
-public class ConfigurationBoundResourceCache<T> {
-
-    private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState<T>>>> mCache =
-            new ArrayMap<String, LongSparseArray<WeakReference<ConstantState<T>>>>();
-
-    final Resources mResources;
+public class ConfigurationBoundResourceCache<T> extends ThemedResourceCache<ConstantState<T>> {
+    private final Resources mResources;
 
     /**
-     * Creates a Resource cache for the given Resources instance.
+     * Creates a cache for the given Resources instance.
      *
-     * @param resources The Resource which can be used when creating new instances.
+     * @param resources the resources to use when creating new instances
      */
     public ConfigurationBoundResourceCache(Resources resources) {
         mResources = resources;
     }
 
     /**
-     * Adds a new item to the cache.
+     * If the resource is cached, creates and returns a new instance of it.
      *
-     * @param key A custom key that uniquely identifies the resource.
-     * @param theme The Theme instance where this resource was loaded.
-     * @param constantState The constant state that can create new instances of the resource.
-     *
+     * @param key a key that uniquely identifies the drawable resource
+     * @param theme the theme where the resource will be used
+     * @return a new instance of the resource, or {@code null} if not in
+     *         the cache
      */
-    public void put(long key, Resources.Theme theme, ConstantState<T> constantState) {
-        if (constantState == null) {
-            return;
-        }
-        final String themeKey = theme == null ? "" : theme.getKey();
-        LongSparseArray<WeakReference<ConstantState<T>>> themedCache;
-        synchronized (this) {
-            themedCache = mCache.get(themeKey);
-            if (themedCache == null) {
-                themedCache = new LongSparseArray<WeakReference<ConstantState<T>>>(1);
-                mCache.put(themeKey, themedCache);
-            }
-            themedCache.put(key, new WeakReference<ConstantState<T>>(constantState));
-        }
-    }
-
-    /**
-     * If the resource is cached, creates a new instance of it and returns.
-     *
-     * @param key The long key which can be used to uniquely identify the resource.
-     * @param theme The The Theme instance where we want to load this resource.
-     *
-     * @return If this resources was loaded before, returns a new instance of it. Otherwise, returns
-     *         null.
-     */
-    public T get(long key, Resources.Theme theme) {
-        final String themeKey = theme != null ? theme.getKey() : "";
-        final LongSparseArray<WeakReference<ConstantState<T>>> themedCache;
-        final WeakReference<ConstantState<T>> wr;
-        synchronized (this) {
-            themedCache = mCache.get(themeKey);
-            if (themedCache == null) {
-                return null;
-            }
-            wr = themedCache.get(key);
-        }
-        if (wr == null) {
-            return null;
-        }
-        final ConstantState entry = wr.get();
+    public T getInstance(long key, Resources.Theme theme) {
+        final ConstantState<T> entry = get(key, theme);
         if (entry != null) {
-            return  (T) entry.newInstance(mResources, theme);
-        } else {  // our entry has been purged
-            synchronized (this) {
-                // there is a potential race condition here where this entry may be put in
-                // another thread. But we prefer it to minimize lock duration
-                themedCache.delete(key);
-            }
+            return entry.newInstance(mResources, theme);
         }
+
         return null;
     }
 
-    /**
-     * Users of ConfigurationBoundResourceCache must call this method whenever a configuration
-     * change happens. On this callback, the cache invalidates all resources that are not valid
-     * anymore.
-     *
-     * @param configChanges The configuration changes
-     */
-    public void onConfigurationChange(final int configChanges) {
-        synchronized (this) {
-            final int size = mCache.size();
-            for (int i = size - 1; i >= 0; i--) {
-                final LongSparseArray<WeakReference<ConstantState<T>>>
-                        themeCache = mCache.valueAt(i);
-                onConfigurationChangeInt(themeCache, configChanges);
-                if (themeCache.size() == 0) {
-                    mCache.removeAt(i);
-                }
-            }
-        }
+    @Override
+    public boolean shouldInvalidateEntry(ConstantState<T> entry, int configChanges) {
+        return Configuration.needNewResources(configChanges, entry.getChangingConfigurations());
     }
-
-    private void onConfigurationChangeInt(
-            final LongSparseArray<WeakReference<ConstantState<T>>> themeCache,
-            final int configChanges) {
-        final int size = themeCache.size();
-        for (int i = size - 1; i >= 0; i--) {
-            final WeakReference<ConstantState<T>> wr = themeCache.valueAt(i);
-            final ConstantState<T> constantState = wr.get();
-            if (constantState == null || Configuration.needNewResources(
-                    configChanges, constantState.getChangingConfigurations())) {
-                themeCache.removeAt(i);
-            }
-        }
-    }
-
 }
diff --git a/core/java/android/content/res/DrawableCache.java b/core/java/android/content/res/DrawableCache.java
new file mode 100644
index 0000000..fc70bc60
--- /dev/null
+++ b/core/java/android/content/res/DrawableCache.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 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 android.content.res;
+
+import android.graphics.drawable.Drawable;
+
+/**
+ * Class which can be used to cache Drawable resources against a theme.
+ */
+class DrawableCache extends ThemedResourceCache<Drawable.ConstantState> {
+    private final Resources mResources;
+
+    /**
+     * Creates a cache for the given Resources instance.
+     *
+     * @param resources the resources to use when creating new instances
+     */
+    public DrawableCache(Resources resources) {
+        mResources = resources;
+    }
+
+    /**
+     * If the resource is cached, creates and returns a new instance of it.
+     *
+     * @param key a key that uniquely identifies the drawable resource
+     * @param theme the theme where the resource will be used
+     * @return a new instance of the resource, or {@code null} if not in
+     *         the cache
+     */
+    public Drawable getInstance(long key, Resources.Theme theme) {
+        final Drawable.ConstantState entry = get(key, theme);
+        if (entry != null) {
+            return entry.newDrawable(mResources, theme);
+        }
+
+        return null;
+    }
+
+    @Override
+    public boolean shouldInvalidateEntry(Drawable.ConstantState entry, int configChanges) {
+        return false;
+    }
+}
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index ae41b69..1d108a2 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -20,6 +20,8 @@
 import android.annotation.ColorInt;
 import android.annotation.StyleRes;
 import android.annotation.StyleableRes;
+
+import com.android.internal.util.GrowingArrayUtils;
 import com.android.internal.util.XmlUtils;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -132,10 +134,8 @@
     // These are protected by mAccessLock.
     private final Object mAccessLock = new Object();
     private final Configuration mTmpConfig = new Configuration();
-    private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> mDrawableCache =
-            new ArrayMap<>();
-    private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> mColorDrawableCache =
-            new ArrayMap<>();
+    private final DrawableCache mDrawableCache = new DrawableCache(this);
+    private final DrawableCache mColorDrawableCache = new DrawableCache(this);
     private final ConfigurationBoundResourceCache<ColorStateList> mColorStateListCache =
             new ConfigurationBoundResourceCache<>(this);
     private final ConfigurationBoundResourceCache<Animator> mAnimatorCache =
@@ -1441,7 +1441,7 @@
             AssetManager.applyThemeStyle(mTheme, resId, force);
 
             mThemeResId = resId;
-            mKey += Integer.toHexString(resId) + (force ? "! " : " ");
+            mKey.append(resId, force);
         }
 
         /**
@@ -1457,7 +1457,7 @@
             AssetManager.copyTheme(mTheme, other.mTheme);
 
             mThemeResId = other.mThemeResId;
-            mKey = other.mKey;
+            mKey.setTo(other.getKey());
         }
 
         /**
@@ -1765,6 +1765,9 @@
             mTheme = mAssets.createTheme();
         }
 
+        /** Unique key for the series of styles applied to this theme. */
+        private final ThemeKey mKey = new ThemeKey();
+
         @SuppressWarnings("hiding")
         private final AssetManager mAssets;
         private final long mTheme;
@@ -1772,9 +1775,6 @@
         /** Resource identifier for the theme. */
         private int mThemeResId = 0;
 
-        /** Unique key for the series of styles applied to this theme. */
-        private String mKey = "";
-
         // Needed by layoutlib.
         /*package*/ long getNativeTheme() {
             return mTheme;
@@ -1784,7 +1784,7 @@
             return mThemeResId;
         }
 
-        /*package*/ String getKey() {
+        /*package*/ ThemeKey getKey() {
             return mKey;
         }
 
@@ -1793,29 +1793,119 @@
         }
 
         /**
-         * Parses {@link #mKey} and returns a String array that holds pairs of adjacent Theme data:
-         * resource name followed by whether or not it was forced, as specified by
-         * {@link #applyStyle(int, boolean)}.
+         * Parses {@link #mKey} and returns a String array that holds pairs of
+         * adjacent Theme data: resource name followed by whether or not it was
+         * forced, as specified by {@link #applyStyle(int, boolean)}.
          *
          * @hide
          */
         @ViewDebug.ExportedProperty(category = "theme", hasAdjacentMapping = true)
         public String[] getTheme() {
-            String[] themeData = mKey.split(" ");
-            String[] themes = new String[themeData.length * 2];
-            String theme;
-            boolean forced;
-
-            for (int i = 0, j = themeData.length - 1; i < themes.length; i += 2, --j) {
-                theme = themeData[j];
-                forced = theme.endsWith("!");
-                themes[i] = forced ?
-                        getResourceNameFromHexString(theme.substring(0, theme.length() - 1)) :
-                        getResourceNameFromHexString(theme);
+            final int N = mKey.mCount;
+            final String[] themes = new String[N * 2];
+            for (int i = 0, j = N - 1; i < themes.length; i += 2, --j) {
+                final int resId = mKey.mResId[i];
+                final boolean forced = mKey.mForce[i];
+                themes[i] = getResourceName(resId);
                 themes[i + 1] = forced ? "forced" : "not forced";
             }
             return themes;
         }
+
+        /**
+         * Rebases the theme against the parent Resource object's current
+         * configuration by re-applying the styles passed to
+         * {@link #applyStyle(int, boolean)}.
+         *
+         * @hide
+         */
+        public void rebase() {
+            AssetManager.clearTheme(mTheme);
+
+            // Reapply the same styles in the same order.
+            for (int i = 0; i < mKey.mCount; i++) {
+                final int resId = mKey.mResId[i];
+                final boolean force = mKey.mForce[i];
+                AssetManager.applyThemeStyle(mTheme, resId, force);
+            }
+        }
+    }
+
+    static class ThemeKey implements Cloneable {
+        int[] mResId;
+        boolean[] mForce;
+        int mCount;
+
+        private int mHashCode = 0;
+
+        public void append(int resId, boolean force) {
+            if (mResId == null) {
+                mResId = new int[4];
+            }
+
+            if (mForce == null) {
+                mForce = new boolean[4];
+            }
+
+            mResId = GrowingArrayUtils.append(mResId, mCount, resId);
+            mForce = GrowingArrayUtils.append(mForce, mCount, force);
+            mCount++;
+
+            mHashCode = 31 * (31 * mHashCode + resId) + (force ? 1 : 0);
+        }
+
+        /**
+         * Sets up this key as a deep copy of another key.
+         *
+         * @param other the key to deep copy into this key
+         */
+        public void setTo(ThemeKey other) {
+            mResId = other.mResId == null ? null : other.mResId.clone();
+            mForce = other.mForce == null ? null : other.mForce.clone();
+            mCount = other.mCount;
+        }
+
+        @Override
+        public int hashCode() {
+            return mHashCode;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+
+            if (o == null || getClass() != o.getClass() || hashCode() != o.hashCode()) {
+                return false;
+            }
+
+            final ThemeKey t = (ThemeKey) o;
+            if (mCount != t.mCount) {
+                return false;
+            }
+
+            final int N = mCount;
+            for (int i = 0; i < N; i++) {
+                if (mResId[i] != t.mResId[i] || mForce[i] != t.mForce[i]) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        /**
+         * @return a shallow copy of this key
+         */
+        @Override
+        public ThemeKey clone() {
+            final ThemeKey other = new ThemeKey();
+            other.mResId = mResId;
+            other.mForce = mForce;
+            other.mCount = mCount;
+            return other;
+        }
     }
 
     /**
@@ -1944,8 +2034,8 @@
                         + " final compat is " + mCompatibilityInfo);
             }
 
-            clearDrawableCachesLocked(mDrawableCache, configChanges);
-            clearDrawableCachesLocked(mColorDrawableCache, configChanges);
+            mDrawableCache.onConfigurationChange(configChanges);
+            mColorDrawableCache.onConfigurationChange(configChanges);
             mColorStateListCache.onConfigurationChange(configChanges);
             mAnimatorCache.onConfigurationChange(configChanges);
             mStateListAnimatorCache.onConfigurationChange(configChanges);
@@ -1983,48 +2073,6 @@
         return configChanges;
     }
 
-    private void clearDrawableCachesLocked(
-            ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
-            int configChanges) {
-        final int N = caches.size();
-        for (int i = 0; i < N; i++) {
-            clearDrawableCacheLocked(caches.valueAt(i), configChanges);
-        }
-    }
-
-    private void clearDrawableCacheLocked(
-            LongSparseArray<WeakReference<ConstantState>> cache, int configChanges) {
-        if (DEBUG_CONFIG) {
-            Log.d(TAG, "Cleaning up drawables config changes: 0x"
-                    + Integer.toHexString(configChanges));
-        }
-        final int N = cache.size();
-        for (int i = 0; i < N; i++) {
-            final WeakReference<ConstantState> ref = cache.valueAt(i);
-            if (ref != null) {
-                final ConstantState cs = ref.get();
-                if (cs != null) {
-                    if (Configuration.needNewResources(
-                            configChanges, cs.getChangingConfigurations())) {
-                        if (DEBUG_CONFIG) {
-                            Log.d(TAG, "FLUSHING #0x"
-                                    + Long.toHexString(cache.keyAt(i))
-                                    + " / " + cs + " with changes: 0x"
-                                    + Integer.toHexString(cs.getChangingConfigurations()));
-                        }
-                        cache.setValueAt(i, null);
-                    } else if (DEBUG_CONFIG) {
-                        Log.d(TAG, "(Keeping #0x"
-                                + Long.toHexString(cache.keyAt(i))
-                                + " / " + cs + " with changes: 0x"
-                                + Integer.toHexString(cs.getChangingConfigurations())
-                                + ")");
-                    }
-                }
-            }
-        }
-    }
-
     /**
      * {@code Locale.toLanguageTag} will transform the obsolete (and deprecated)
      * language codes "in", "ji" and "iw" to "id", "yi" and "he" respectively.
@@ -2436,7 +2484,7 @@
         }
 
         final boolean isColorDrawable;
-        final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches;
+        final DrawableCache caches;
         final long key;
         if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
                 && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
@@ -2452,7 +2500,7 @@
         // First, check whether we have a cached version of this drawable
         // that was inflated against the specified theme.
         if (!mPreloading) {
-            final Drawable cachedDrawable = getCachedDrawable(caches, key, theme);
+            final Drawable cachedDrawable = caches.getInstance(key, theme);
             if (cachedDrawable != null) {
                 return cachedDrawable;
             }
@@ -2479,13 +2527,8 @@
         // Determine if the drawable has unresolved theme attributes. If it
         // does, we'll need to apply a theme and store it in a theme-specific
         // cache.
-        final String cacheKey;
-        if (!dr.canApplyTheme()) {
-            cacheKey = CACHE_NOT_THEMED;
-        } else if (theme == null) {
-            cacheKey = CACHE_NULL_THEME;
-        } else {
-            cacheKey = theme.getKey();
+        final boolean canApplyTheme = dr.canApplyTheme();
+        if (canApplyTheme && theme != null) {
             dr = dr.mutate();
             dr.applyTheme(theme);
             dr.clearMutated();
@@ -2495,15 +2538,14 @@
         // cache: preload, not themed, null theme, or theme-specific.
         if (dr != null) {
             dr.setChangingConfigurations(value.changingConfigurations);
-            cacheDrawable(value, isColorDrawable, caches, cacheKey, key, dr);
+            cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr);
         }
 
         return dr;
     }
 
-    private void cacheDrawable(TypedValue value, boolean isColorDrawable,
-            ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
-            String cacheKey, long key, Drawable dr) {
+    private void cacheDrawable(TypedValue value, boolean isColorDrawable, DrawableCache caches,
+            Theme theme, boolean usesTheme, long key, Drawable dr) {
         final ConstantState cs = dr.getConstantState();
         if (cs == null) {
             return;
@@ -2531,54 +2573,12 @@
             }
         } else {
             synchronized (mAccessLock) {
-                LongSparseArray<WeakReference<ConstantState>> themedCache = caches.get(cacheKey);
-                if (themedCache == null) {
-                    // Clean out the caches before we add more. This shouldn't
-                    // happen very often.
-                    pruneCaches(caches);
-                    themedCache = new LongSparseArray<>(1);
-                    caches.put(cacheKey, themedCache);
-                }
-                themedCache.put(key, new WeakReference<>(cs));
+                caches.put(key, theme, cs, usesTheme);
             }
         }
     }
 
     /**
-     * Prunes empty caches from the cache map.
-     *
-     * @param caches The map of caches to prune.
-     */
-    private void pruneCaches(ArrayMap<String,
-            LongSparseArray<WeakReference<ConstantState>>> caches) {
-        final int N = caches.size();
-        for (int i = N - 1; i >= 0; i--) {
-            final LongSparseArray<WeakReference<ConstantState>> cache = caches.valueAt(i);
-            if (pruneCache(cache)) {
-                caches.removeAt(i);
-            }
-        }
-    }
-
-    /**
-     * Prunes obsolete weak references from a cache, returning {@code true} if
-     * the cache is empty and should be removed.
-     *
-     * @param cache The cache of weak references to prune.
-     * @return {@code true} if the cache is empty and should be removed.
-     */
-    private boolean pruneCache(LongSparseArray<WeakReference<ConstantState>> cache) {
-        final int N = cache.size();
-        for (int i = N - 1; i >= 0; i--) {
-            final WeakReference entry = cache.valueAt(i);
-            if (entry == null || entry.get() == null) {
-                cache.removeAt(i);
-            }
-        }
-        return cache.size() == 0;
-    }
-
-    /**
      * Loads a drawable from XML or resources stream.
      */
     private Drawable loadDrawableForCookie(TypedValue value, int id, Theme theme) {
@@ -2631,51 +2631,6 @@
         return dr;
     }
 
-    private Drawable getCachedDrawable(
-            ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
-            long key, Theme theme) {
-        synchronized (mAccessLock) {
-            // First search theme-agnostic cache.
-            final Drawable unthemedDrawable = getCachedDrawableLocked(
-                    caches, key, CACHE_NOT_THEMED);
-            if (unthemedDrawable != null) {
-                return unthemedDrawable;
-            }
-
-            // Next search theme-specific cache.
-            final String themeKey = theme != null ? theme.getKey() : CACHE_NULL_THEME;
-            return getCachedDrawableLocked(caches, key, themeKey);
-        }
-    }
-
-    private Drawable getCachedDrawableLocked(
-            ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
-            long key, String themeKey) {
-        final LongSparseArray<WeakReference<ConstantState>> cache = caches.get(themeKey);
-        if (cache != null) {
-            final ConstantState entry = getConstantStateLocked(cache, key);
-            if (entry != null) {
-                return entry.newDrawable(this);
-            }
-        }
-        return null;
-    }
-
-    private ConstantState getConstantStateLocked(
-            LongSparseArray<WeakReference<ConstantState>> drawableCache, long key) {
-        final WeakReference<ConstantState> wr = drawableCache.get(key);
-        if (wr != null) {
-            final ConstantState entry = wr.get();
-            if (entry != null) {
-                return entry;
-            } else {
-                // Our entry has been purged.
-                drawableCache.delete(key);
-            }
-        }
-        return null;
-    }
-
     @Nullable
     ColorStateList loadColorStateList(TypedValue value, int id, Theme theme)
             throws NotFoundException {
@@ -2713,8 +2668,7 @@
         }
 
         final ConfigurationBoundResourceCache<ColorStateList> cache = mColorStateListCache;
-
-        csl = cache.get(key, theme);
+        csl = cache.getInstance(key, theme);
         if (csl != null) {
             return csl;
         }
diff --git a/core/java/android/content/res/ThemedResourceCache.java b/core/java/android/content/res/ThemedResourceCache.java
new file mode 100644
index 0000000..9a2d06147
--- /dev/null
+++ b/core/java/android/content/res/ThemedResourceCache.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2015 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 android.content.res;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.Resources.Theme;
+import android.content.res.Resources.ThemeKey;
+import android.util.LongSparseArray;
+import android.util.ArrayMap;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Data structure used for caching data against themes.
+ *
+ * @param <T> type of data to cache
+ */
+abstract class ThemedResourceCache<T> {
+    private ArrayMap<ThemeKey, LongSparseArray<WeakReference<T>>> mThemedEntries;
+    private LongSparseArray<WeakReference<T>> mUnthemedEntries;
+    private LongSparseArray<WeakReference<T>> mNullThemedEntries;
+
+    /**
+     * Adds a new theme-dependent entry to the cache.
+     *
+     * @param key a key that uniquely identifies the entry
+     * @param theme the theme against which this entry was inflated, or
+     *              {@code null} if the entry has no theme applied
+     * @param entry the entry to cache
+     */
+    public void put(long key, @Nullable Theme theme, @NonNull T entry) {
+        put(key, theme, entry, true);
+    }
+
+    /**
+     * Adds a new entry to the cache.
+     *
+     * @param key a key that uniquely identifies the entry
+     * @param theme the theme against which this entry was inflated, or
+     *              {@code null} if the entry has no theme applied
+     * @param entry the entry to cache
+     * @param usesTheme {@code true} if the entry is affected theme changes,
+     *                  {@code false} otherwise
+     */
+    public void put(long key, @Nullable Theme theme, @NonNull T entry, boolean usesTheme) {
+        if (entry == null) {
+            return;
+        }
+
+        synchronized (this) {
+            final LongSparseArray<WeakReference<T>> entries;
+            if (!usesTheme) {
+                entries = getUnthemedLocked(true);
+            } else {
+                entries = getThemedLocked(theme, true);
+            }
+            if (entries != null) {
+                entries.put(key, new WeakReference<>(entry));
+            }
+        }
+    }
+
+    /**
+     * Returns an entry from the cache.
+     *
+     * @param key a key that uniquely identifies the entry
+     * @param theme the theme where the entry will be used
+     * @return a cached entry, or {@code null} if not in the cache
+     */
+    @Nullable
+    public T get(long key, @Nullable Theme theme) {
+        // The themed (includes null-themed) and unthemed caches are mutually
+        // exclusive, so we'll give priority to whichever one we think we'll
+        // hit first. Since most of the framework drawables are themed, that's
+        // probably going to be the themed cache.
+        synchronized (this) {
+            final LongSparseArray<WeakReference<T>> themedEntries = getThemedLocked(theme, false);
+            if (themedEntries != null) {
+                final WeakReference<T> themedEntry = themedEntries.get(key);
+                if (themedEntry != null) {
+                    return themedEntry.get();
+                }
+            }
+
+            final LongSparseArray<WeakReference<T>> unthemedEntries = getUnthemedLocked(false);
+            if (unthemedEntries != null) {
+                final WeakReference<T> unthemedEntry = unthemedEntries.get(key);
+                if (unthemedEntry != null) {
+                    return unthemedEntry.get();
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Prunes cache entries that have been invalidated by a configuration
+     * change.
+     *
+     * @param configChanges a bitmask of configuration changes
+     */
+    public void onConfigurationChange(int configChanges) {
+        prune(configChanges);
+    }
+
+    /**
+     * Returns whether a cached entry has been invalidated by a configuration
+     * change.
+     *
+     * @param entry a cached entry
+     * @param configChanges a non-zero bitmask of configuration changes
+     * @return {@code true} if the entry is invalid, {@code false} otherwise
+     */
+    protected abstract boolean shouldInvalidateEntry(@NonNull T entry, int configChanges);
+
+    /**
+     * Returns the cached data for the specified theme, optionally creating a
+     * new entry if one does not already exist.
+     *
+     * @param t the theme for which to return cached data
+     * @param create {@code true} to create an entry if one does not already
+     *               exist, {@code false} otherwise
+     * @return the cached data for the theme, or {@code null} if the cache is
+     *         empty and {@code create} was {@code false}
+     */
+    @Nullable
+    private LongSparseArray<WeakReference<T>> getThemedLocked(@Nullable Theme t, boolean create) {
+        if (t == null) {
+            if (mNullThemedEntries == null && create) {
+                mNullThemedEntries = new LongSparseArray<>(1);
+            }
+            return mNullThemedEntries;
+        }
+
+        if (mThemedEntries == null) {
+            if (create) {
+                mThemedEntries = new ArrayMap<>(1);
+            } else {
+                return null;
+            }
+        }
+
+        final ThemeKey key = t.getKey();
+        LongSparseArray<WeakReference<T>> cache = mThemedEntries.get(key);
+        if (cache == null && create) {
+            cache = new LongSparseArray<>(1);
+
+            final ThemeKey keyClone = key.clone();
+            mThemedEntries.put(keyClone, cache);
+        }
+
+        return cache;
+    }
+
+    /**
+     * Returns the theme-agnostic cached data.
+     *
+     * @param create {@code true} to create an entry if one does not already
+     *               exist, {@code false} otherwise
+     * @return the theme-agnostic cached data, or {@code null} if the cache is
+     *         empty and {@code create} was {@code false}
+     */
+    @Nullable
+    private LongSparseArray<WeakReference<T>> getUnthemedLocked(boolean create) {
+        if (mUnthemedEntries == null && create) {
+            mUnthemedEntries = new LongSparseArray<>(1);
+        }
+        return mUnthemedEntries;
+    }
+
+    /**
+     * Prunes cache entries affected by configuration changes or where weak
+     * references have expired.
+     *
+     * @param configChanges a bitmask of configuration changes, or {@code 0} to
+     *                      simply prune missing weak references
+     * @return {@code true} if the cache is completely empty after pruning
+     */
+    private boolean prune(int configChanges) {
+        synchronized (this) {
+            if (mThemedEntries != null) {
+                for (int i = mThemedEntries.size() - 1; i >= 0; i--) {
+                    if (pruneEntriesLocked(mThemedEntries.valueAt(i), configChanges)) {
+                        mThemedEntries.removeAt(i);
+                    }
+                }
+            }
+
+            pruneEntriesLocked(mNullThemedEntries, configChanges);
+            pruneEntriesLocked(mUnthemedEntries, configChanges);
+
+            return mThemedEntries == null && mNullThemedEntries == null
+                    && mUnthemedEntries == null;
+        }
+    }
+
+    private boolean pruneEntriesLocked(@Nullable LongSparseArray<WeakReference<T>> entries,
+            int configChanges) {
+        if (entries == null) {
+            return true;
+        }
+
+        for (int i = entries.size() - 1; i >= 0; i--) {
+            final WeakReference<T> ref = entries.valueAt(i);
+            if (ref == null || pruneEntryLocked(ref.get(), configChanges)) {
+                entries.removeAt(i);
+            }
+        }
+
+        return entries.size() == 0;
+    }
+
+    private boolean pruneEntryLocked(@Nullable T entry, int configChanges) {
+        return entry == null || (configChanges != 0
+                && shouldInvalidateEntry(entry, configChanges));
+    }
+}
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index aeddf03..ef71c42 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -212,8 +212,7 @@
      * <p>All capture sessions can be used for capturing images from the camera but only capture
      * sessions created by
      * {@link CameraDevice#createReprocessibleCaptureSession createReprocessibleCaptureSession}
-     * can submit reprocess capture requests. The list of requests must all be capturing images from
-     * the camera or all be reprocess capture requests. Submitting a reprocess request to a regular
+     * can submit reprocess capture requests. Submitting a reprocess request to a regular
      * capture session will result in an {@link IllegalArgumentException}.</p>
      *
      * @param requests the list of settings for this burst capture
@@ -236,9 +235,7 @@
      * @throws IllegalArgumentException If the requests target no Surfaces, or the requests target
      *                                  Surfaces not currently configured as outputs; or a reprocess
      *                                  capture request is submitted in a non-reprocessible capture
-     *                                  session; or the list of requests contains both requests to
-     *                                  capture images from the camera and reprocess capture
-     *                                  requests; or one of the reprocess capture requests was
+     *                                  session; or one of the reprocess capture requests was
      *                                  created with a {@link TotalCaptureResult} from a different
      *                                  session; or one of the captures targets a Surface in the
      *                                  middle of being {@link #prepare prepared}; or if the handler
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index 3c19529..dff6227 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -177,26 +177,20 @@
     public synchronized int captureBurst(List<CaptureRequest> requests, CaptureCallback callback,
             Handler handler) throws CameraAccessException {
         if (requests == null) {
-            throw new IllegalArgumentException("requests must not be null");
+            throw new IllegalArgumentException("Requests must not be null");
         } else if (requests.isEmpty()) {
-            throw new IllegalArgumentException("requests must have at least one element");
+            throw new IllegalArgumentException("Requests must have at least one element");
         }
 
-        boolean reprocess = requests.get(0).isReprocess();
-        if (reprocess && !isReprocessible()) {
-            throw new IllegalArgumentException("this capture session cannot handle reprocess " +
-                    "requests");
-        } else if (reprocess && requests.get(0).getReprocessibleSessionId() != mId) {
-            throw new IllegalArgumentException("capture request was created for another session");
-        }
-
-        for (int i = 1; i < requests.size(); i++) {
-            if (requests.get(i).isReprocess() != reprocess) {
-                throw new IllegalArgumentException("cannot mix regular and reprocess capture " +
-                        " requests");
-            } else if (reprocess && requests.get(i).getReprocessibleSessionId() != mId) {
-                throw new IllegalArgumentException("capture request was created for another " +
-                    "session");
+        for (CaptureRequest request : requests) {
+            if (request.isReprocess()) {
+                if (!isReprocessible()) {
+                    throw new IllegalArgumentException("This capture session cannot handle " +
+                            "reprocess requests");
+                } else if (request.getReprocessibleSessionId() != mId) {
+                    throw new IllegalArgumentException("Capture request was created for another " +
+                            "session");
+                }
             }
         }
 
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index ff4ad79..e84b46a 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -94,11 +94,11 @@
     private final int mTotalPartialCount;
 
     /**
-     * A list tracking request and its expected last frame.
-     * Updated when calling ICameraDeviceUser methods.
+     * A list tracking request and its expected last regular frame number and last reprocess frame
+     * number. Updated when calling ICameraDeviceUser methods.
      */
-    private final List<SimpleEntry</*frameNumber*/Long, /*requestId*/Integer>>
-            mFrameNumberRequestPairs = new ArrayList<SimpleEntry<Long, Integer>>();
+    private final List<RequestLastFrameNumbersHolder> mRequestLastFrameNumbersList =
+            new ArrayList<>();
 
     /**
      * An object tracking received frame numbers.
@@ -653,8 +653,8 @@
      *
      * <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never
      * sent to HAL. Then onCaptureSequenceAborted is immediately triggered.
-     * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber pair
-     * is added to the list mFrameNumberRequestPairs.</p>
+     * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber as the last
+     * regular frame number will be added to the list mRequestLastFrameNumbersList.</p>
      *
      * @param requestId the request ID of the current repeating request.
      *
@@ -693,10 +693,6 @@
                                         "early trigger sequence complete for request %d",
                                         requestId));
                             }
-                            if (lastFrameNumber < Integer.MIN_VALUE
-                                    || lastFrameNumber > Integer.MAX_VALUE) {
-                                throw new AssertionError(lastFrameNumber + " cannot be cast to int");
-                            }
                             holder.getCallback().onCaptureSequenceAborted(
                                     CameraDeviceImpl.this,
                                     requestId);
@@ -710,9 +706,11 @@
                         requestId));
             }
         } else {
-            mFrameNumberRequestPairs.add(
-                    new SimpleEntry<Long, Integer>(lastFrameNumber,
-                            requestId));
+            // This function is only called for regular request so lastFrameNumber is the last
+            // regular frame number.
+            mRequestLastFrameNumbersList.add(new RequestLastFrameNumbersHolder(requestId,
+                    lastFrameNumber));
+
             // It is possible that the last frame has already arrived, so we need to check
             // for sequence completion right away
             checkAndFireSequenceComplete();
@@ -779,8 +777,8 @@
                 }
                 mRepeatingRequestId = requestId;
             } else {
-                mFrameNumberRequestPairs.add(
-                        new SimpleEntry<Long, Integer>(lastFrameNumber, requestId));
+                mRequestLastFrameNumbersList.add(new RequestLastFrameNumbersHolder(requestList,
+                        requestId, lastFrameNumber));
             }
 
             if (mIdle) {
@@ -1146,7 +1144,101 @@
         public int getSessionId() {
             return mSessionId;
         }
+    }
 
+    /**
+     * This class holds a capture ID and its expected last regular frame number and last reprocess
+     * frame number.
+     */
+    static class RequestLastFrameNumbersHolder {
+        // request ID
+        private final int mRequestId;
+        // The last regular frame number for this request ID. It's
+        // CaptureCallback.NO_FRAMES_CAPTURED if the request ID has no regular request.
+        private final long mLastRegularFrameNumber;
+        // The last reprocess frame number for this request ID. It's
+        // CaptureCallback.NO_FRAMES_CAPTURED if the request ID has no reprocess request.
+        private final long mLastReprocessFrameNumber;
+
+        /**
+         * Create a request-last-frame-numbers holder with a list of requests, request ID, and
+         * the last frame number returned by camera service.
+         */
+        public RequestLastFrameNumbersHolder(List<CaptureRequest> requestList, int requestId,
+                long lastFrameNumber) {
+            long lastRegularFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
+            long lastReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
+            long frameNumber = lastFrameNumber;
+
+            if (lastFrameNumber < requestList.size() - 1) {
+                throw new IllegalArgumentException("lastFrameNumber: " + lastFrameNumber +
+                        " should be at least " + (requestList.size() - 1) + " for the number of " +
+                        " requests in the list: " + requestList.size());
+            }
+
+            // find the last regular frame number and the last reprocess frame number
+            for (int i = requestList.size() - 1; i >= 0; i--) {
+                CaptureRequest request = requestList.get(i);
+                if (request.isReprocess() && lastReprocessFrameNumber ==
+                        CaptureCallback.NO_FRAMES_CAPTURED) {
+                    lastReprocessFrameNumber = frameNumber;
+                } else if (!request.isReprocess() && lastRegularFrameNumber ==
+                        CaptureCallback.NO_FRAMES_CAPTURED) {
+                    lastRegularFrameNumber = frameNumber;
+                }
+
+                if (lastReprocessFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED &&
+                        lastRegularFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED) {
+                    break;
+                }
+
+                frameNumber--;
+            }
+
+            mLastRegularFrameNumber = lastRegularFrameNumber;
+            mLastReprocessFrameNumber = lastReprocessFrameNumber;
+            mRequestId = requestId;
+        }
+
+        /**
+         * Create a request-last-frame-numbers holder with a request ID and last regular frame
+         * number.
+         */
+        public RequestLastFrameNumbersHolder(int requestId, long lastRegularFrameNumber) {
+            mLastRegularFrameNumber = lastRegularFrameNumber;
+            mLastReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
+            mRequestId = requestId;
+        }
+
+        /**
+         * Return the last regular frame number. Return CaptureCallback.NO_FRAMES_CAPTURED if
+         * it contains no regular request.
+         */
+        public long getLastRegularFrameNumber() {
+            return mLastRegularFrameNumber;
+        }
+
+        /**
+         * Return the last reprocess frame number. Return CaptureCallback.NO_FRAMES_CAPTURED if
+         * it contains no reprocess request.
+         */
+        public long getLastReprocessFrameNumber() {
+            return mLastReprocessFrameNumber;
+        }
+
+        /**
+         * Return the last frame number overall.
+         */
+        public long getLastFrameNumber() {
+            return Math.max(mLastRegularFrameNumber, mLastReprocessFrameNumber);
+        }
+
+        /**
+         * Return the request ID.
+         */
+        public int getRequestId() {
+            return mRequestId;
+        }
     }
 
     /**
@@ -1154,8 +1246,8 @@
      */
     public class FrameNumberTracker {
 
-        private long mCompletedFrameNumber = -1;
-        private long mCompletedReprocessFrameNumber = -1;
+        private long mCompletedFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
+        private long mCompletedReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
         /** the skipped frame numbers that belong to regular results */
         private final LinkedList<Long> mSkippedRegularFrameNumbers = new LinkedList<Long>();
         /** the skipped frame numbers that belong to reprocess results */
@@ -1360,11 +1452,11 @@
         long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber();
         long completedReprocessFrameNumber = mFrameNumberTracker.getCompletedReprocessFrameNumber();
         boolean isReprocess = false;
-        Iterator<SimpleEntry<Long, Integer> > iter = mFrameNumberRequestPairs.iterator();
+        Iterator<RequestLastFrameNumbersHolder> iter = mRequestLastFrameNumbersList.iterator();
         while (iter.hasNext()) {
-            final SimpleEntry<Long, Integer> frameNumberRequestPair = iter.next();
+            final RequestLastFrameNumbersHolder requestLastFrameNumbers = iter.next();
             boolean sequenceCompleted = false;
-            final int requestId = frameNumberRequestPair.getValue();
+            final int requestId = requestLastFrameNumbers.getRequestId();
             final CaptureCallbackHolder holder;
             synchronized(mInterfaceLock) {
                 if (mRemoteDevice == null) {
@@ -1376,19 +1468,22 @@
                 holder = (index >= 0) ?
                         mCaptureCallbackMap.valueAt(index) : null;
                 if (holder != null) {
-                    isReprocess = holder.getRequest().isReprocess();
+                    long lastRegularFrameNumber =
+                            requestLastFrameNumbers.getLastRegularFrameNumber();
+                    long lastReprocessFrameNumber =
+                            requestLastFrameNumbers.getLastReprocessFrameNumber();
+
                     // check if it's okay to remove request from mCaptureCallbackMap
-                    if ((isReprocess && frameNumberRequestPair.getKey() <=
-                            completedReprocessFrameNumber) || (!isReprocess &&
-                            frameNumberRequestPair.getKey() <= completedFrameNumber)) {
+                    if (lastRegularFrameNumber <= completedFrameNumber &&
+                            lastReprocessFrameNumber <= completedReprocessFrameNumber) {
                         sequenceCompleted = true;
                         mCaptureCallbackMap.removeAt(index);
                         if (DEBUG) {
                             Log.v(TAG, String.format(
-                                    "remove holder for requestId %d, "
-                                    + "because lastFrame %d is <= %d",
-                                    requestId, frameNumberRequestPair.getKey(),
-                                    completedFrameNumber));
+                                    "Remove holder for requestId %d, because lastRegularFrame %d " +
+                                    "is <= %d and lastReprocessFrame %d is <= %d", requestId,
+                                    lastRegularFrameNumber, completedFrameNumber,
+                                    lastReprocessFrameNumber, completedReprocessFrameNumber));
                         }
                     }
                 }
@@ -1412,16 +1507,10 @@
                                         requestId));
                             }
 
-                            long lastFrameNumber = frameNumberRequestPair.getKey();
-                            if (lastFrameNumber < Integer.MIN_VALUE
-                                    || lastFrameNumber > Integer.MAX_VALUE) {
-                                throw new AssertionError(lastFrameNumber
-                                        + " cannot be cast to int");
-                            }
                             holder.getCallback().onCaptureSequenceCompleted(
                                 CameraDeviceImpl.this,
                                 requestId,
-                                lastFrameNumber);
+                                requestLastFrameNumbers.getLastFrameNumber());
                         }
                     }
                 };
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index ebc2aac..1b25505 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -1639,6 +1639,7 @@
         for (int i = 0; i < count; i++) {
             TransitionValues values = lookIn.get(i);
             if (values == null) {
+                // Null values are always added to the end of the list, so we know to stop now.
                 return null;
             }
             if (values.view == view) {
@@ -1742,6 +1743,9 @@
                     View oldView = oldInfo.view;
                     TransitionValues startValues = getTransitionValues(oldView, true);
                     TransitionValues endValues = getMatchedTransitionValues(oldView, true);
+                    if (startValues == null && endValues == null) {
+                        endValues = mEndValues.viewValues.get(oldView);
+                    }
                     boolean cancel = (startValues != null || endValues != null) &&
                             oldInfo.transition.areValuesChanged(oldValues, endValues);
                     if (cancel) {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 712fdba..c10ac74 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -3903,10 +3903,6 @@
         public void updatePosition(float x, float y) {
             final int trueOffset = mTextView.getOffsetForPosition(x, y);
             final int currLine = mTextView.getLineAtCoordinate(y);
-
-            // Don't select white space on different lines.
-            if (isWhitespaceLine(mPrevLine, currLine, trueOffset)) return;
-
             boolean positionCursor = false;
             int offset = trueOffset;
             int end = getWordEnd(offset, true);
@@ -4005,10 +4001,6 @@
         public void updatePosition(float x, float y) {
             final int trueOffset = mTextView.getOffsetForPosition(x, y);
             final int currLine = mTextView.getLineAtCoordinate(y);
-
-            // Don't select white space on different lines.
-            if (isWhitespaceLine(mPrevLine, currLine, trueOffset)) return;
-
             int offset = trueOffset;
             boolean positionCursor = false;
 
@@ -4070,36 +4062,6 @@
     }
 
     /**
-     * Checks whether selection is happening on a different line than previous and
-     * if that line only contains whitespace up to the touch location.
-     *
-     * @param prevLine The previous line the selection was on.
-     * @param currLine The current line being selected.
-     * @param offset The offset in the text where the touch occurred.
-     * @return Whether or not it was just a white space line being selected.
-     */
-    private boolean isWhitespaceLine(int prevLine, int currLine, int offset) {
-        if (prevLine == currLine) {
-            // Same line; don't care.
-            return false;
-        }
-        CharSequence text = mTextView.getText();
-        if (offset == text.length()) {
-            // No character at the last position.
-            return false;
-        }
-        int lineEndOffset = mTextView.getLayout().getLineEnd(currLine);
-        for (int cp, i = offset; i < lineEndOffset; i += Character.charCount(cp)) {
-            cp = Character.codePointAt(text, i);
-            if (!Character.isSpaceChar(cp) && !Character.isWhitespace(cp)) {
-                // There are non white space chars on the line.
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
      * A CursorController instance can be used to control a cursor in the text.
      */
     private interface CursorController extends ViewTreeObserver.OnTouchModeChangeListener {
@@ -4178,8 +4140,6 @@
         private int mStartOffset = -1;
         // Indicates whether the user is selecting text and using the drag accelerator.
         private boolean mDragAcceleratorActive;
-        // Indicates the line of text the drag accelerator is on.
-        private int mPrevLine = -1;
 
         SelectionModifierCursorController() {
             resetTouchOffsets();
@@ -4272,8 +4232,6 @@
                         }
                     }
 
-                    // New selection, reset line.
-                    mPrevLine = mTextView.getLineAtCoordinate(y);
                     mDownPositionX = x;
                     mDownPositionY = y;
                     mGestureStayedInTapRegion = true;
@@ -4330,13 +4288,6 @@
                             if (my > fingerOffset) my -= fingerOffset;
                             offset = mTextView.getOffsetForPosition(mx, my);
 
-                            int currLine = mTextView.getLineAtCoordinate(my);
-
-                            // Don't select white space on different lines.
-                            if (isWhitespaceLine(mPrevLine, currLine, offset)) return;
-
-                            mPrevLine = currLine;
-
                             // Perform the check for closeness at edge of view, if we're very close
                             // don't adjust the offset to be in front of the finger - otherwise the
                             // user can't select words at the edge.
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index 05866f0..94b9416 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -1791,8 +1791,9 @@
 
     private class ResizePopupRunnable implements Runnable {
         public void run() {
-            if (mDropDownList != null && mDropDownList.getCount() > mDropDownList.getChildCount() &&
-                    mDropDownList.getChildCount() <= mListItemExpandMaximum) {
+            if (mDropDownList != null && mDropDownList.isAttachedToWindow()
+                    && mDropDownList.getCount() > mDropDownList.getChildCount()
+                    && mDropDownList.getChildCount() <= mListItemExpandMaximum) {
                 mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
                 show();
             }
diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java
index 6173832..9277f9b 100644
--- a/core/java/com/android/internal/logging/MetricsLogger.java
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.os.Build;
+import android.view.View;
 
 /**
  * Log all the things.
@@ -33,6 +34,10 @@
     public static final int ACTION_BAN_APP_NOTES = 146;
     public static final int NOTIFICATION_ZEN_MODE_EVENT_RULE = 147;
     public static final int ACTION_DISMISS_ALL_NOTES = 148;
+    public static final int QS_DND_DETAILS = 149;
+    public static final int QS_BLUETOOTH_DETAILS = 150;
+    public static final int QS_CAST_DETAILS = 151;
+    public static final int QS_WIFI_DETAILS = 152;
 
     public static void visible(Context context, int category) throws IllegalArgumentException {
         if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) {
@@ -41,13 +46,27 @@
         EventLogTags.writeSysuiViewVisibility(category, 100);
     }
 
-    public static void hidden(Context context, int category) {
+    public static void hidden(Context context, int category) throws IllegalArgumentException {
         if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) {
             throw new IllegalArgumentException("Must define metric category");
         }
         EventLogTags.writeSysuiViewVisibility(category, 0);
     }
 
+    public static void visibility(Context context, int category, boolean visibile)
+            throws IllegalArgumentException {
+        if (visibile) {
+            visible(context, category);
+        } else {
+            hidden(context, category);
+        }
+    }
+
+    public static void visibility(Context context, int category, int vis)
+            throws IllegalArgumentException {
+        visibility(context, category, vis == View.VISIBLE);
+    }
+
     public static void action(Context context, int category) {
         action(context, category, "");
     }
diff --git a/core/java/com/android/internal/transition/EpicenterClipReveal.java b/core/java/com/android/internal/transition/EpicenterClipReveal.java
deleted file mode 100644
index 1a6736a..0000000
--- a/core/java/com/android/internal/transition/EpicenterClipReveal.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright (C) 2015 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.internal.transition;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.RectEvaluator;
-import android.animation.TimeInterpolator;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Rect;
-import android.transition.TransitionValues;
-import android.transition.Visibility;
-import android.util.AttributeSet;
-import android.util.Property;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.AnimationUtils;
-import android.view.animation.PathInterpolator;
-
-import com.android.internal.R;
-
-/**
- * EpicenterClipReveal captures the {@link View#getClipBounds()} before and
- * after the scene change and animates between those and the epicenter bounds
- * during a visibility transition.
- */
-public class EpicenterClipReveal extends Visibility {
-    private static final String PROPNAME_CLIP = "android:epicenterReveal:clip";
-    private static final String PROPNAME_BOUNDS = "android:epicenterReveal:bounds";
-
-    private final TimeInterpolator mInterpolatorX;
-    private final TimeInterpolator mInterpolatorY;
-    private final boolean mCenterClipBounds;
-
-    public EpicenterClipReveal() {
-        mInterpolatorX = null;
-        mInterpolatorY = null;
-        mCenterClipBounds = false;
-    }
-
-    public EpicenterClipReveal(Context context, AttributeSet attrs) {
-        super(context, attrs);
-
-        final TypedArray a = context.obtainStyledAttributes(attrs,
-                R.styleable.EpicenterClipReveal, 0, 0);
-
-        mCenterClipBounds = a.getBoolean(R.styleable.EpicenterClipReveal_centerClipBounds, false);
-
-        final int interpolatorX = a.getResourceId(R.styleable.EpicenterClipReveal_interpolatorX, 0);
-        if (interpolatorX != 0) {
-            mInterpolatorX = AnimationUtils.loadInterpolator(context, interpolatorX);
-        } else {
-            mInterpolatorX = TransitionConstants.LINEAR_OUT_SLOW_IN;
-        }
-
-        final int interpolatorY = a.getResourceId(R.styleable.EpicenterClipReveal_interpolatorY, 0);
-        if (interpolatorY != 0) {
-            mInterpolatorY = AnimationUtils.loadInterpolator(context, interpolatorY);
-        } else {
-            mInterpolatorY = TransitionConstants.FAST_OUT_SLOW_IN;
-        }
-
-        a.recycle();
-    }
-
-    @Override
-    public void captureStartValues(TransitionValues transitionValues) {
-        super.captureStartValues(transitionValues);
-        captureValues(transitionValues);
-    }
-
-    @Override
-    public void captureEndValues(TransitionValues transitionValues) {
-        super.captureEndValues(transitionValues);
-        captureValues(transitionValues);
-    }
-
-    private void captureValues(TransitionValues values) {
-        final View view = values.view;
-        if (view.getVisibility() == View.GONE) {
-            return;
-        }
-
-        final Rect clip = view.getClipBounds();
-        values.values.put(PROPNAME_CLIP, clip);
-
-        if (clip == null) {
-            final Rect bounds = new Rect(0, 0, view.getWidth(), view.getHeight());
-            values.values.put(PROPNAME_BOUNDS, bounds);
-        }
-    }
-
-    @Override
-    public Animator onAppear(ViewGroup sceneRoot, View view,
-            TransitionValues startValues, TransitionValues endValues) {
-        if (endValues == null) {
-            return null;
-        }
-
-        final Rect end = getBestRect(endValues);
-        final Rect start = getEpicenterOrCenter(end);
-
-        // Prepare the view.
-        view.setClipBounds(start);
-
-        return createRectAnimator(view, start, end, endValues, mInterpolatorX, mInterpolatorY);
-    }
-
-    @Override
-    public Animator onDisappear(ViewGroup sceneRoot, View view,
-            TransitionValues startValues, TransitionValues endValues) {
-        if (startValues == null) {
-            return null;
-        }
-
-        final Rect start = getBestRect(startValues);
-        final Rect end = getEpicenterOrCenter(start);
-
-        // Prepare the view.
-        view.setClipBounds(start);
-
-        return createRectAnimator(view, start, end, endValues, mInterpolatorX, mInterpolatorY);
-    }
-
-    private Rect getEpicenterOrCenter(Rect bestRect) {
-        final Rect epicenter = getEpicenter();
-        if (epicenter != null) {
-            // Translate the clip bounds to be centered within the target bounds.
-            if (mCenterClipBounds) {
-                final int offsetX = bestRect.centerX() - epicenter.centerX();
-                final int offsetY = bestRect.centerY() - epicenter.centerY();
-                epicenter.offset(offsetX, offsetY);
-            }
-            return epicenter;
-        }
-
-        final int centerX = bestRect.centerX();
-        final int centerY = bestRect.centerY();
-        return new Rect(centerX, centerY, centerX, centerY);
-    }
-
-    private Rect getBestRect(TransitionValues values) {
-        final Rect clipRect = (Rect) values.values.get(PROPNAME_CLIP);
-        if (clipRect == null) {
-            return (Rect) values.values.get(PROPNAME_BOUNDS);
-        }
-        return clipRect;
-    }
-
-    private static Animator createRectAnimator(final View view, Rect start, Rect end,
-            TransitionValues endValues, TimeInterpolator interpolatorX,
-            TimeInterpolator interpolatorY) {
-        final RectEvaluator evaluator = new RectEvaluator(new Rect());
-        final Rect terminalClip = (Rect) endValues.values.get(PROPNAME_CLIP);
-
-        final ClipDimenProperty propX = new ClipDimenProperty(ClipDimenProperty.TARGET_X);
-        final ObjectAnimator animX = ObjectAnimator.ofObject(view, propX, evaluator, start, end);
-        if (interpolatorX != null) {
-            animX.setInterpolator(interpolatorX);
-        }
-
-        final ClipDimenProperty propY = new ClipDimenProperty(ClipDimenProperty.TARGET_Y);
-        final ObjectAnimator animY = ObjectAnimator.ofObject(view, propY, evaluator, start, end);
-        if (interpolatorY != null) {
-            animY.setInterpolator(interpolatorY);
-        }
-
-        final AnimatorSet animSet = new AnimatorSet();
-        animSet.playTogether(animX, animY);
-        animSet.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                view.setClipBounds(terminalClip);
-            }
-        });
-        return animSet;
-    }
-
-    private static class ClipDimenProperty extends Property<View, Rect> {
-        public static final char TARGET_X = 'x';
-        public static final char TARGET_Y = 'y';
-
-        private final Rect mTempRect = new Rect();
-
-        private final int mTargetDimension;
-
-        public ClipDimenProperty(char targetDimension) {
-            super(Rect.class, "clip_bounds_" + targetDimension);
-
-            mTargetDimension = targetDimension;
-        }
-
-        @Override
-        public Rect get(View object) {
-            final Rect tempRect = mTempRect;
-            if (!object.getClipBounds(tempRect)) {
-                tempRect.setEmpty();
-            }
-            return tempRect;
-        }
-
-        @Override
-        public void set(View object, Rect value) {
-            final Rect tempRect = mTempRect;
-            if (object.getClipBounds(tempRect)) {
-                if (mTargetDimension == TARGET_X) {
-                    tempRect.left = value.left;
-                    tempRect.right = value.right;
-                } else {
-                    tempRect.top = value.top;
-                    tempRect.bottom = value.bottom;
-                }
-                object.setClipBounds(tempRect);
-            }
-        }
-    }
-}
diff --git a/core/java/com/android/internal/transition/EpicenterTranslate.java b/core/java/com/android/internal/transition/EpicenterTranslate.java
deleted file mode 100644
index eac3ff8..0000000
--- a/core/java/com/android/internal/transition/EpicenterTranslate.java
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Copyright (C) 2015 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.internal.transition;
-
-import com.android.internal.R;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Rect;
-import android.transition.TransitionValues;
-import android.transition.Visibility;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.AnimationUtils;
-
-/**
- * EpicenterTranslate captures the {@link View#getTranslationX()} and
- * {@link View#getTranslationY()} before and after the scene change and
- * animates between those and the epicenter's center during a visibility
- * transition.
- */
-public class EpicenterTranslate extends Visibility {
-    private static final String PROPNAME_BOUNDS = "android:epicenterReveal:bounds";
-    private static final String PROPNAME_TRANSLATE_X = "android:epicenterReveal:translateX";
-    private static final String PROPNAME_TRANSLATE_Y = "android:epicenterReveal:translateY";
-    private static final String PROPNAME_TRANSLATE_Z = "android:epicenterReveal:translateZ";
-    private static final String PROPNAME_Z = "android:epicenterReveal:z";
-
-    private final TimeInterpolator mInterpolatorX;
-    private final TimeInterpolator mInterpolatorY;
-    private final TimeInterpolator mInterpolatorZ;
-
-    public EpicenterTranslate() {
-        mInterpolatorX = null;
-        mInterpolatorY = null;
-        mInterpolatorZ = null;
-    }
-
-    public EpicenterTranslate(Context context, AttributeSet attrs) {
-        super(context, attrs);
-
-        final TypedArray a = context.obtainStyledAttributes(attrs,
-                R.styleable.EpicenterTranslate, 0, 0);
-
-        final int interpolatorX = a.getResourceId(R.styleable.EpicenterTranslate_interpolatorX, 0);
-        if (interpolatorX != 0) {
-            mInterpolatorX = AnimationUtils.loadInterpolator(context, interpolatorX);
-        } else {
-            mInterpolatorX = TransitionConstants.FAST_OUT_SLOW_IN;
-        }
-
-        final int interpolatorY = a.getResourceId(R.styleable.EpicenterTranslate_interpolatorY, 0);
-        if (interpolatorY != 0) {
-            mInterpolatorY = AnimationUtils.loadInterpolator(context, interpolatorY);
-        } else {
-            mInterpolatorY = TransitionConstants.FAST_OUT_SLOW_IN;
-        }
-
-        final int interpolatorZ = a.getResourceId(R.styleable.EpicenterTranslate_interpolatorZ, 0);
-        if (interpolatorZ != 0) {
-            mInterpolatorZ = AnimationUtils.loadInterpolator(context, interpolatorZ);
-        } else {
-            mInterpolatorZ = TransitionConstants.FAST_OUT_SLOW_IN;
-        }
-
-        a.recycle();
-    }
-
-    @Override
-    public void captureStartValues(TransitionValues transitionValues) {
-        super.captureStartValues(transitionValues);
-        captureValues(transitionValues);
-    }
-
-    @Override
-    public void captureEndValues(TransitionValues transitionValues) {
-        super.captureEndValues(transitionValues);
-        captureValues(transitionValues);
-    }
-
-    private void captureValues(TransitionValues values) {
-        final View view = values.view;
-        if (view.getVisibility() == View.GONE) {
-            return;
-        }
-
-        final Rect bounds = new Rect(0, 0, view.getWidth(), view.getHeight());
-        values.values.put(PROPNAME_BOUNDS, bounds);
-        values.values.put(PROPNAME_TRANSLATE_X, view.getTranslationX());
-        values.values.put(PROPNAME_TRANSLATE_Y, view.getTranslationY());
-        values.values.put(PROPNAME_TRANSLATE_Z, view.getTranslationZ());
-        values.values.put(PROPNAME_Z, view.getZ());
-    }
-
-    @Override
-    public Animator onAppear(ViewGroup sceneRoot, View view,
-            TransitionValues startValues, TransitionValues endValues) {
-        if (endValues == null) {
-            return null;
-        }
-
-        final Rect end = (Rect) endValues.values.get(PROPNAME_BOUNDS);
-        final Rect start = getEpicenterOrCenter(end);
-        final float startX = start.centerX() - end.centerX();
-        final float startY = start.centerY() - end.centerY();
-        final float startZ = 0 - (float) endValues.values.get(PROPNAME_Z);
-
-        // Translate the view to be centered on the epicenter.
-        view.setTranslationX(startX);
-        view.setTranslationY(startY);
-        view.setTranslationZ(startZ);
-
-        final float endX = (float) endValues.values.get(PROPNAME_TRANSLATE_X);
-        final float endY = (float) endValues.values.get(PROPNAME_TRANSLATE_Y);
-        final float endZ = (float) endValues.values.get(PROPNAME_TRANSLATE_Z);
-        return createAnimator(view, startX, startY, startZ, endX, endY, endZ,
-                mInterpolatorX, mInterpolatorY, mInterpolatorZ);
-    }
-
-    @Override
-    public Animator onDisappear(ViewGroup sceneRoot, View view,
-            TransitionValues startValues, TransitionValues endValues) {
-        if (startValues == null) {
-            return null;
-        }
-
-        final Rect start = (Rect) endValues.values.get(PROPNAME_BOUNDS);
-        final Rect end = getEpicenterOrCenter(start);
-        final float endX = end.centerX() - start.centerX();
-        final float endY = end.centerY() - start.centerY();
-        final float endZ = 0 - (float) startValues.values.get(PROPNAME_Z);
-
-        final float startX = (float) endValues.values.get(PROPNAME_TRANSLATE_X);
-        final float startY = (float) endValues.values.get(PROPNAME_TRANSLATE_Y);
-        final float startZ = (float) endValues.values.get(PROPNAME_TRANSLATE_Z);
-        return createAnimator(view, startX, startY, startZ, endX, endY, endZ,
-                mInterpolatorX, mInterpolatorY, mInterpolatorZ);
-    }
-
-    private Rect getEpicenterOrCenter(Rect bestRect) {
-        final Rect epicenter = getEpicenter();
-        if (epicenter != null) {
-            return epicenter;
-        }
-
-        final int centerX = bestRect.centerX();
-        final int centerY = bestRect.centerY();
-        return new Rect(centerX, centerY, centerX, centerY);
-    }
-
-    private static Animator createAnimator(final View view, float startX, float startY,
-            float startZ, float endX, float endY, float endZ, TimeInterpolator interpolatorX,
-            TimeInterpolator interpolatorY, TimeInterpolator interpolatorZ) {
-        final ObjectAnimator animX = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, startX, endX);
-        if (interpolatorX != null) {
-            animX.setInterpolator(interpolatorX);
-        }
-
-        final ObjectAnimator animY = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, startY, endY);
-        if (interpolatorY != null) {
-            animY.setInterpolator(interpolatorY);
-        }
-
-        final ObjectAnimator animZ = ObjectAnimator.ofFloat(view, View.TRANSLATION_Z, startZ, endZ);
-        if (interpolatorZ != null) {
-            animZ.setInterpolator(interpolatorZ);
-        }
-
-        final AnimatorSet animSet = new AnimatorSet();
-        animSet.playTogether(animX, animY, animZ);
-        return animSet;
-    }
-}
diff --git a/core/java/com/android/internal/transition/EpicenterTranslateClipReveal.java b/core/java/com/android/internal/transition/EpicenterTranslateClipReveal.java
new file mode 100644
index 0000000..2c10297
--- /dev/null
+++ b/core/java/com/android/internal/transition/EpicenterTranslateClipReveal.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2015 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.internal.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.animation.TypeEvaluator;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Rect;
+import android.transition.TransitionValues;
+import android.transition.Visibility;
+import android.util.AttributeSet;
+import android.util.Property;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AnimationUtils;
+
+import com.android.internal.R;
+
+/**
+ * EpicenterTranslateClipReveal captures the clip bounds and translation values
+ * before and after the scene change and animates between those and the
+ * epicenter bounds during a visibility transition.
+ */
+public class EpicenterTranslateClipReveal extends Visibility {
+    private static final String PROPNAME_CLIP = "android:epicenterReveal:clip";
+    private static final String PROPNAME_BOUNDS = "android:epicenterReveal:bounds";
+    private static final String PROPNAME_TRANSLATE_X = "android:epicenterReveal:translateX";
+    private static final String PROPNAME_TRANSLATE_Y = "android:epicenterReveal:translateY";
+    private static final String PROPNAME_TRANSLATE_Z = "android:epicenterReveal:translateZ";
+    private static final String PROPNAME_Z = "android:epicenterReveal:z";
+
+    private final TimeInterpolator mInterpolatorX;
+    private final TimeInterpolator mInterpolatorY;
+    private final TimeInterpolator mInterpolatorZ;
+
+    public EpicenterTranslateClipReveal() {
+        mInterpolatorX = null;
+        mInterpolatorY = null;
+        mInterpolatorZ = null;
+    }
+
+    public EpicenterTranslateClipReveal(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        final TypedArray a = context.obtainStyledAttributes(attrs,
+                R.styleable.EpicenterTranslateClipReveal, 0, 0);
+
+        final int interpolatorX = a.getResourceId(
+                R.styleable.EpicenterTranslateClipReveal_interpolatorX, 0);
+        if (interpolatorX != 0) {
+            mInterpolatorX = AnimationUtils.loadInterpolator(context, interpolatorX);
+        } else {
+            mInterpolatorX = TransitionConstants.LINEAR_OUT_SLOW_IN;
+        }
+
+        final int interpolatorY = a.getResourceId(
+                R.styleable.EpicenterTranslateClipReveal_interpolatorY, 0);
+        if (interpolatorY != 0) {
+            mInterpolatorY = AnimationUtils.loadInterpolator(context, interpolatorY);
+        } else {
+            mInterpolatorY = TransitionConstants.FAST_OUT_SLOW_IN;
+        }
+
+        final int interpolatorZ = a.getResourceId(
+                R.styleable.EpicenterTranslateClipReveal_interpolatorZ, 0);
+        if (interpolatorZ != 0) {
+            mInterpolatorZ = AnimationUtils.loadInterpolator(context, interpolatorZ);
+        } else {
+            mInterpolatorZ = TransitionConstants.FAST_OUT_SLOW_IN;
+        }
+
+        a.recycle();
+    }
+
+    @Override
+    public void captureStartValues(TransitionValues transitionValues) {
+        super.captureStartValues(transitionValues);
+        captureValues(transitionValues);
+    }
+
+    @Override
+    public void captureEndValues(TransitionValues transitionValues) {
+        super.captureEndValues(transitionValues);
+        captureValues(transitionValues);
+    }
+
+    private void captureValues(TransitionValues values) {
+        final View view = values.view;
+        if (view.getVisibility() == View.GONE) {
+            return;
+        }
+
+        final Rect bounds = new Rect(0, 0, view.getWidth(), view.getHeight());
+        values.values.put(PROPNAME_BOUNDS, bounds);
+        values.values.put(PROPNAME_TRANSLATE_X, view.getTranslationX());
+        values.values.put(PROPNAME_TRANSLATE_Y, view.getTranslationY());
+        values.values.put(PROPNAME_TRANSLATE_Z, view.getTranslationZ());
+        values.values.put(PROPNAME_Z, view.getZ());
+
+        final Rect clip = view.getClipBounds();
+        values.values.put(PROPNAME_CLIP, clip);
+    }
+
+    @Override
+    public Animator onAppear(ViewGroup sceneRoot, View view,
+            TransitionValues startValues, TransitionValues endValues) {
+        if (endValues == null) {
+            return null;
+        }
+
+        final Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
+        final Rect startBounds = getEpicenterOrCenter(endBounds);
+        final float startX = startBounds.centerX() - endBounds.centerX();
+        final float startY = startBounds.centerY() - endBounds.centerY();
+        final float startZ = 0 - (float) endValues.values.get(PROPNAME_Z);
+
+        // Translate the view to be centered on the epicenter.
+        view.setTranslationX(startX);
+        view.setTranslationY(startY);
+        view.setTranslationZ(startZ);
+
+        final float endX = (float) endValues.values.get(PROPNAME_TRANSLATE_X);
+        final float endY = (float) endValues.values.get(PROPNAME_TRANSLATE_Y);
+        final float endZ = (float) endValues.values.get(PROPNAME_TRANSLATE_Z);
+
+        final Rect endClip = getBestRect(endValues);
+        final Rect startClip = getEpicenterOrCenter(endClip);
+
+        // Prepare the view.
+        view.setClipBounds(startClip);
+
+        final State startStateX = new State(startClip.left, startClip.right, startX);
+        final State endStateX = new State(endClip.left, endClip.right, endX);
+        final State startStateY = new State(startClip.top, startClip.bottom, startY);
+        final State endStateY = new State(endClip.top, endClip.bottom, endY);
+
+        return createRectAnimator(view, startStateX, startStateY, startZ, endStateX, endStateY,
+                endZ, endValues, mInterpolatorX, mInterpolatorY, mInterpolatorZ);
+    }
+
+    @Override
+    public Animator onDisappear(ViewGroup sceneRoot, View view,
+            TransitionValues startValues, TransitionValues endValues) {
+        if (startValues == null) {
+            return null;
+        }
+
+        final Rect startBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
+        final Rect endBounds = getEpicenterOrCenter(startBounds);
+        final float endX = endBounds.centerX() - startBounds.centerX();
+        final float endY = endBounds.centerY() - startBounds.centerY();
+        final float endZ = 0 - (float) startValues.values.get(PROPNAME_Z);
+
+        final float startX = (float) endValues.values.get(PROPNAME_TRANSLATE_X);
+        final float startY = (float) endValues.values.get(PROPNAME_TRANSLATE_Y);
+        final float startZ = (float) endValues.values.get(PROPNAME_TRANSLATE_Z);
+
+        final Rect startClip = getBestRect(startValues);
+        final Rect endClip = getEpicenterOrCenter(startClip);
+
+        // Prepare the view.
+        view.setClipBounds(startClip);
+
+        final State startStateX = new State(startClip.left, startClip.right, startX);
+        final State endStateX = new State(endClip.left, endClip.right, endX);
+        final State startStateY = new State(startClip.top, startClip.bottom, startY);
+        final State endStateY = new State(endClip.top, endClip.bottom, endY);
+
+        return createRectAnimator(view, startStateX, startStateY, startZ, endStateX, endStateY,
+                endZ, endValues, mInterpolatorX, mInterpolatorY, mInterpolatorZ);
+    }
+
+    private Rect getEpicenterOrCenter(Rect bestRect) {
+        final Rect epicenter = getEpicenter();
+        if (epicenter != null) {
+            return epicenter;
+        }
+
+        final int centerX = bestRect.centerX();
+        final int centerY = bestRect.centerY();
+        return new Rect(centerX, centerY, centerX, centerY);
+    }
+
+    private Rect getBestRect(TransitionValues values) {
+        final Rect clipRect = (Rect) values.values.get(PROPNAME_CLIP);
+        if (clipRect == null) {
+            return (Rect) values.values.get(PROPNAME_BOUNDS);
+        }
+        return clipRect;
+    }
+
+    private static Animator createRectAnimator(final View view, State startX, State startY,
+            float startZ, State endX, State endY, float endZ, TransitionValues endValues,
+            TimeInterpolator interpolatorX, TimeInterpolator interpolatorY,
+            TimeInterpolator interpolatorZ) {
+        final StateEvaluator evaluator = new StateEvaluator();
+
+        final ObjectAnimator animZ = ObjectAnimator.ofFloat(view, View.TRANSLATION_Z, startZ, endZ);
+        if (interpolatorZ != null) {
+            animZ.setInterpolator(interpolatorZ);
+        }
+
+        final StateProperty propX = new StateProperty(StateProperty.TARGET_X);
+        final ObjectAnimator animX = ObjectAnimator.ofObject(view, propX, evaluator, startX, endX);
+        if (interpolatorX != null) {
+            animX.setInterpolator(interpolatorX);
+        }
+
+        final StateProperty propY = new StateProperty(StateProperty.TARGET_Y);
+        final ObjectAnimator animY = ObjectAnimator.ofObject(view, propY, evaluator, startY, endY);
+        if (interpolatorY != null) {
+            animY.setInterpolator(interpolatorY);
+        }
+
+        final Rect terminalClip = (Rect) endValues.values.get(PROPNAME_CLIP);
+        final AnimatorListenerAdapter animatorListener = new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                view.setClipBounds(terminalClip);
+            }
+        };
+
+        final AnimatorSet animSet = new AnimatorSet();
+        animSet.playTogether(animX, animY, animZ);
+        animSet.addListener(animatorListener);
+        return animSet;
+    }
+
+    private static class State {
+        int lower;
+        int upper;
+        float trans;
+
+        public State() {}
+
+        public State(int lower, int upper, float trans) {
+            this.lower = lower;
+            this.upper = upper;
+            this.trans = trans;
+        }
+    }
+
+    private static class StateEvaluator implements TypeEvaluator<State> {
+        private final State mTemp = new State();
+
+        @Override
+        public State evaluate(float fraction, State startValue, State endValue) {
+            mTemp.upper = startValue.upper + (int) ((endValue.upper - startValue.upper) * fraction);
+            mTemp.lower = startValue.lower + (int) ((endValue.lower - startValue.lower) * fraction);
+            mTemp.trans = startValue.trans + (int) ((endValue.trans - startValue.trans) * fraction);
+            return mTemp;
+        }
+    }
+
+    private static class StateProperty extends Property<View, State> {
+        public static final char TARGET_X = 'x';
+        public static final char TARGET_Y = 'y';
+
+        private final Rect mTempRect = new Rect();
+        private final State mTempState = new State();
+
+        private final int mTargetDimension;
+
+        public StateProperty(char targetDimension) {
+            super(State.class, "state_" + targetDimension);
+
+            mTargetDimension = targetDimension;
+        }
+
+        @Override
+        public State get(View object) {
+            final Rect tempRect = mTempRect;
+            if (!object.getClipBounds(tempRect)) {
+                tempRect.setEmpty();
+            }
+            final State tempState = mTempState;
+            if (mTargetDimension == TARGET_X) {
+                tempState.trans = object.getTranslationX();
+                tempState.lower = tempRect.left + (int) tempState.trans;
+                tempState.upper = tempRect.right + (int) tempState.trans;
+            } else {
+                tempState.trans = object.getTranslationY();
+                tempState.lower = tempRect.top + (int) tempState.trans;
+                tempState.upper = tempRect.bottom + (int) tempState.trans;
+            }
+            return tempState;
+        }
+
+        @Override
+        public void set(View object, State value) {
+            final Rect tempRect = mTempRect;
+            if (object.getClipBounds(tempRect)) {
+                if (mTargetDimension == TARGET_X) {
+                    tempRect.left = value.lower - (int) value.trans;
+                    tempRect.right = value.upper - (int) value.trans;
+                } else {
+                    tempRect.top = value.lower - (int) value.trans;
+                    tempRect.bottom = value.upper - (int) value.trans;
+                }
+                object.setClipBounds(tempRect);
+            }
+
+            if (mTargetDimension == TARGET_X) {
+                object.setTranslationX(value.trans);
+            } else {
+                object.setTranslationY(value.trans);
+            }
+        }
+    }
+}
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 9babe82..2785c48 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -224,7 +224,7 @@
 }
 
 void Bitmap::reconfigure(const SkImageInfo& info) {
-    reconfigure(info, mPixelRef->rowBytes(), mPixelRef->colorTable());
+    reconfigure(info, info.minRowBytes(), nullptr);
 }
 
 void Bitmap::detachFromJava() {
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index db495dd..74a9e4e 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -976,6 +976,12 @@
     dest->setTo(*src);
 }
 
+static void android_content_AssetManager_clearTheme(JNIEnv* env, jobject clazz, jlong themeHandle)
+{
+    ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
+    theme->clear();
+}
+
 static jint android_content_AssetManager_loadThemeAttributeValue(
     JNIEnv* env, jobject clazz, jlong themeHandle, jint ident, jobject outValue, jboolean resolve)
 {
@@ -2108,6 +2114,8 @@
         (void*) android_content_AssetManager_applyThemeStyle },
     { "copyTheme", "(JJ)V",
         (void*) android_content_AssetManager_copyTheme },
+    { "clearTheme", "(J)V",
+        (void*) android_content_AssetManager_clearTheme },
     { "loadThemeAttributeValue", "(JILandroid/util/TypedValue;Z)I",
         (void*) android_content_AssetManager_loadThemeAttributeValue },
     { "getThemeChangingConfigurations", "(J)I",
diff --git a/core/res/res/drawable-hdpi/text_cursor_mtrl_alpha.9.png b/core/res/res/drawable-hdpi/text_cursor_mtrl_alpha.9.png
deleted file mode 100644
index 0179433..0000000
--- a/core/res/res/drawable-hdpi/text_cursor_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_cursor_mtrl_alpha.9.png b/core/res/res/drawable-mdpi/text_cursor_mtrl_alpha.9.png
deleted file mode 100644
index e5760be..0000000
--- a/core/res/res/drawable-mdpi/text_cursor_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/text_cursor_mtrl_alpha.9.png b/core/res/res/drawable-xhdpi/text_cursor_mtrl_alpha.9.png
deleted file mode 100644
index 3939214..0000000
--- a/core/res/res/drawable-xhdpi/text_cursor_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/text_cursor_mtrl_alpha.9.png b/core/res/res/drawable-xxhdpi/text_cursor_mtrl_alpha.9.png
deleted file mode 100644
index 432c385..0000000
--- a/core/res/res/drawable-xxhdpi/text_cursor_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/text_cursor_material.xml b/core/res/res/drawable/text_cursor_material.xml
index a350c47..0bedaa9 100644
--- a/core/res/res/drawable/text_cursor_material.xml
+++ b/core/res/res/drawable/text_cursor_material.xml
@@ -14,6 +14,15 @@
      limitations under the License.
 -->
 
-<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/text_cursor_mtrl_alpha"
-    android:tint="?attr/colorControlActivated" />
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+       android:inset="2dp">
+    <shape
+        android:tint="?attr/colorControlActivated"
+        android:shape="rectangle">
+        <size
+            android:height="2dp"
+            android:width="2dp" />
+        <solid
+            android:color="@color/white" />
+    </shape>
+</inset>
diff --git a/core/res/res/layout-land/time_picker_material.xml b/core/res/res/layout-land/time_picker_material.xml
index 89c3749..4b544d2 100644
--- a/core/res/res/layout-land/time_picker_material.xml
+++ b/core/res/res/layout-land/time_picker_material.xml
@@ -16,7 +16,6 @@
 -->
 
 <GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
-            xmlns:tools="http://schemas.android.com/tools"
             android:layout_width="match_parent"
             android:layout_height="match_parent">
 
@@ -27,8 +26,7 @@
         android:layout_column="0"
         android:layout_row="0"
         android:layout_rowSpan="3"
-        android:layout_gravity="center|fill"
-        tools:background="@color/accent_material_light" />
+        android:layout_gravity="center|fill" />
 
     <RelativeLayout
         android:layout_width="wrap_content"
@@ -56,20 +54,14 @@
                 android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
                 android:singleLine="true"
                 android:ellipsize="none"
-                android:gravity="right"
-                tools:text="23"
-                tools:textSize="@dimen/timepicker_time_label_size"
-                tools:textColor="@color/white" />
+                android:gravity="right" />
 
             <TextView
                 android:id="@+id/separator"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
-                android:importantForAccessibility="no"
-                tools:text=":"
-                tools:textSize="@dimen/timepicker_time_label_size"
-                tools:textColor="@color/white" />
+                android:importantForAccessibility="no" />
 
             <!-- The minutes should always be to the right of the separator,
                  regardless of the current locale's layout direction. -->
@@ -80,10 +72,7 @@
                 android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
                 android:singleLine="true"
                 android:ellipsize="none"
-                android:gravity="left"
-                tools:text="59"
-                tools:textSize="@dimen/timepicker_time_label_size"
-                tools:textColor="@color/white" />
+                android:gravity="left" />
         </LinearLayout>
 
         <!-- The layout alignment of this view will switch between toRightOf
@@ -106,10 +95,7 @@
                 android:paddingTop="@dimen/timepicker_am_top_padding"
                 android:lines="1"
                 android:ellipsize="none"
-                android:includeFontPadding="false"
-                tools:text="AM"
-                tools:textSize="@dimen/timepicker_ampm_label_size"
-                tools:textColor="@color/white" />
+                android:includeFontPadding="false" />
 
             <CheckedTextView
                 android:id="@+id/pm_label"
@@ -121,10 +107,7 @@
                 android:paddingTop="@dimen/timepicker_pm_top_padding"
                 android:lines="1"
                 android:ellipsize="none"
-                android:includeFontPadding="false"
-                tools:text="PM"
-                tools:textSize="@dimen/timepicker_ampm_label_size"
-                tools:textColor="@color/white" />
+                android:includeFontPadding="false" />
         </LinearLayout>
     </RelativeLayout>
 
diff --git a/core/res/res/layout/date_picker_header_material.xml b/core/res/res/layout/date_picker_header_material.xml
index 2150341..a4388f6 100644
--- a/core/res/res/layout/date_picker_header_material.xml
+++ b/core/res/res/layout/date_picker_header_material.xml
@@ -16,17 +16,13 @@
 -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              xmlns:tools="http://schemas.android.com/tools"
               android:id="@+id/date_picker_header"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:paddingBottom="18dp"
               android:paddingStart="?attr/dialogPreferredPadding"
               android:paddingEnd="?attr/dialogPreferredPadding"
-              android:orientation="vertical"
-              tools:background="@color/accent_material_light"
-              tools:paddingStart="24dp"
-              tools:paddingEnd="24dp">
+              android:orientation="vertical">
 
     <!-- Top padding should stay on this view so that
          the touch target is a bit larger. -->
@@ -35,10 +31,7 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:paddingTop="16dp"
-        android:textAppearance="@style/TextAppearance.Material.DatePicker.YearLabel"
-        tools:text="2015"
-        tools:textSize="@dimen/date_picker_year_label_size"
-        tools:textColor="@color/white" />
+        android:textAppearance="@style/TextAppearance.Material.DatePicker.YearLabel" />
 
     <TextView
         android:id="@+id/date_picker_header_date"
@@ -47,9 +40,6 @@
         android:textAppearance="@style/TextAppearance.Material.DatePicker.DateLabel"
         android:gravity="start"
         android:maxLines="2"
-        android:ellipsize="none"
-        tools:text="Thu, Sep 30"
-        tools:textSize="@dimen/date_picker_date_label_size"
-        tools:textColor="@color/white" />
+        android:ellipsize="none" />
 
 </LinearLayout>
diff --git a/core/res/res/layout/time_picker_header_material.xml b/core/res/res/layout/time_picker_header_material.xml
index be9e443..3f5e300 100644
--- a/core/res/res/layout/time_picker_header_material.xml
+++ b/core/res/res/layout/time_picker_header_material.xml
@@ -18,13 +18,11 @@
 <!-- This layout is duplicated in land/time_picker_material.xml, so any
      changes made here need to be manually copied over. -->
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-                xmlns:tools="http://schemas.android.com/tools"
                 android:id="@+id/time_header"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
                 android:orientation="horizontal"
-                android:padding="@dimen/timepicker_separator_padding"
-                tools:background="@color/accent_material_light">
+                android:padding="@dimen/timepicker_separator_padding">
 
     <!-- The hour should always be to the left of the separator,
          regardless of the current locale's layout direction. -->
@@ -37,10 +35,7 @@
         android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
         android:singleLine="true"
         android:ellipsize="none"
-        android:gravity="right"
-        tools:text="23"
-        tools:textSize="@dimen/timepicker_time_label_size"
-        tools:textColor="@color/white" />
+        android:gravity="right" />
 
     <TextView
         android:id="@+id/separator"
@@ -50,10 +45,7 @@
         android:layout_marginRight="@dimen/timepicker_separator_padding"
         android:layout_centerInParent="true"
         android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
-        android:importantForAccessibility="no"
-        tools:text=":"
-        tools:textSize="@dimen/timepicker_time_label_size"
-        tools:textColor="@color/white" />
+        android:importantForAccessibility="no" />
 
     <!-- The minutes should always be to the left of the separator,
          regardless of the current locale's layout direction. -->
@@ -66,10 +58,7 @@
         android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
         android:singleLine="true"
         android:ellipsize="none"
-        android:gravity="left"
-        tools:text="59"
-        tools:textSize="@dimen/timepicker_time_label_size"
-        tools:textColor="@color/white" />
+        android:gravity="left" />
 
     <!-- The layout alignment of this view will switch between toRightOf
          @id/minutes and toLeftOf @id/hours depending on the locale. -->
@@ -90,10 +79,7 @@
             android:paddingTop="@dimen/timepicker_am_top_padding"
             android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
             android:lines="1"
-            android:ellipsize="none"
-            tools:text="AM"
-            tools:textSize="@dimen/timepicker_ampm_label_size"
-            tools:textColor="@color/white" />
+            android:ellipsize="none" />
         <CheckedTextView
             android:id="@+id/pm_label"
             android:layout_width="wrap_content"
@@ -103,9 +89,6 @@
             android:paddingTop="@dimen/timepicker_pm_top_padding"
             android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
             android:lines="1"
-            android:ellipsize="none"
-            tools:text="PM"
-            tools:textSize="@dimen/timepicker_ampm_label_size"
-            tools:textColor="@color/white" />
+            android:ellipsize="none" />
     </LinearLayout>
 </RelativeLayout>
diff --git a/core/res/res/transition/popup_window_enter.xml b/core/res/res/transition/popup_window_enter.xml
index 38c41f0..c4c8dac 100644
--- a/core/res/res/transition/popup_window_enter.xml
+++ b/core/res/res/transition/popup_window_enter.xml
@@ -16,17 +16,14 @@
 
 <transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
                android:transitionOrdering="together">
-    <!-- Start from location of epicenter, move to popup location. -->
-    <transition
-        class="com.android.internal.transition.EpicenterTranslate"
-        android:duration="300" />
-
     <!-- Start from size of epicenter, expand to full width/height. -->
     <transition
-        class="com.android.internal.transition.EpicenterClipReveal"
-        android:centerClipBounds="true"
-        android:duration="300" />
+        class="com.android.internal.transition.EpicenterTranslateClipReveal"
+        android:duration="250" />
 
     <!-- Quickly fade in. -->
-    <fade android:duration="100" />
+    <fade
+        android:duration="100"
+        android:fromAlpha="0.1"
+        android:toAlpha="1.0" />
 </transitionSet>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 1c4b5f7..eaa6278 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5877,16 +5877,9 @@
     </declare-styleable>
 
     <!-- @hide For internal use only. Use only as directed. -->
-    <declare-styleable name="EpicenterClipReveal">
-        <attr name="centerClipBounds" format="boolean" />
+    <declare-styleable name="EpicenterTranslateClipReveal">
         <attr name="interpolatorX" format="reference" />
         <attr name="interpolatorY" format="reference" />
-    </declare-styleable>
-
-    <!-- @hide For internal use only. Use only as directed. -->
-    <declare-styleable name="EpicenterTranslate">
-        <attr name="interpolatorX" />
-        <attr name="interpolatorY" />
         <attr name="interpolatorZ" format="reference" />
     </declare-styleable>
 
diff --git a/graphics/java/android/graphics/Atlas.java b/graphics/java/android/graphics/Atlas.java
index 39a5a53..e0a5345 100644
--- a/graphics/java/android/graphics/Atlas.java
+++ b/graphics/java/android/graphics/Atlas.java
@@ -21,10 +21,12 @@
  */
 public class Atlas {
     /**
-     * This flag indicates whether the packing algorithm will attempt
-     * to rotate entries to make them fit better in the atlas.
+     * WARNING: These flag values are part of the on-disk configuration information,
+     * do not change their values.
      */
-    public static final int FLAG_ALLOW_ROTATIONS = 0x1;
+
+    /** DELETED: FLAG_ROTATION = 0x01 */
+
     /**
      * This flag indicates whether the packing algorithm should leave
      * an empty 1 pixel wide border around each bitmap. This border can
@@ -52,9 +54,7 @@
 
     /**
      * Represents a bitmap packed in the atlas. Each entry has a location in
-     * pixels in the atlas and a rotation flag. If the entry was rotated, the
-     * bitmap must be rotated by 90 degrees (in either direction as long as
-     * the origin remains the same) before being rendered into the atlas.
+     * pixels in the atlas and a rotation flag.
      */
     public static class Entry {
         /**
@@ -65,11 +65,6 @@
          * Location, in pixels, of the bitmap on the Y axis in the atlas.
          */
         public int y;
-
-        /**
-         * If true, the bitmap must be rotated 90 degrees in the atlas.
-         */
-        public boolean rotated;
     }
 
     private final Policy mPolicy;
@@ -239,7 +234,6 @@
 
         private final SplitDecision mSplitDecision;
 
-        private final boolean mAllowRotation;
         private final int mPadding;
 
         /**
@@ -263,7 +257,6 @@
         }
 
         SlicePolicy(int width, int height, int flags, SplitDecision splitDecision) {
-            mAllowRotation = (flags & FLAG_ALLOW_ROTATIONS) != 0;
             mPadding = (flags & FLAG_ADD_PADDING) != 0 ? 1 : 0;
 
             // The entire atlas is empty at first, minus padding
@@ -360,26 +353,9 @@
          *
          * @return True if the rectangle was packed in the atlas, false otherwise
          */
-        @SuppressWarnings("SuspiciousNameCombination")
         private boolean insert(Cell cell, Cell prev, int width, int height, Entry entry) {
-            boolean rotated = false;
-
-            // If the rectangle doesn't fit we'll try to rotate it
-            // if possible before giving up
             if (cell.width < width || cell.height < height) {
-                if (mAllowRotation) {
-                    if (cell.width < height || cell.height < width) {
-                        return false;
-                    }
-
-                    // Rotate the rectangle
-                    int temp = width;
-                    width = height;
-                    height = temp;
-                    rotated = true;
-                } else {
-                    return false;
-                }
+                return false;
             }
 
             // Remaining free space after packing the rectangle
@@ -433,7 +409,6 @@
             // Return the location and rotation of the packed rectangle
             entry.x = cell.x;
             entry.y = cell.y;
-            entry.rotated = rotated;
 
             return true;
         }
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index 587e7fa..b15caeb 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -1631,6 +1631,7 @@
 
         status_t applyStyle(uint32_t resID, bool force=false);
         status_t setTo(const Theme& other);
+        status_t clear();
 
         /**
          * Retrieve a value in the theme.  If the theme defines this
diff --git a/keystore/java/android/security/AndroidKeyPairGenerator.java b/keystore/java/android/security/AndroidKeyPairGenerator.java
index 3b25ba6..3f29c6a 100644
--- a/keystore/java/android/security/AndroidKeyPairGenerator.java
+++ b/keystore/java/android/security/AndroidKeyPairGenerator.java
@@ -54,13 +54,13 @@
 
     public static class RSA extends AndroidKeyPairGenerator {
         public RSA() {
-            super("RSA");
+            super(KeyStoreKeyProperties.Algorithm.RSA);
         }
     }
 
     public static class EC extends AndroidKeyPairGenerator {
         public EC() {
-            super("EC");
+            super(KeyStoreKeyProperties.Algorithm.EC);
         }
     }
 
@@ -83,15 +83,15 @@
     private android.security.KeyStore mKeyStore;
 
     private KeyPairGeneratorSpec mSpec;
-    private String mKeyAlgorithm;
+    private @KeyStoreKeyProperties.AlgorithmEnum String mKeyAlgorithm;
     private int mKeyType;
     private int mKeySize;
 
-    protected AndroidKeyPairGenerator(String algorithm) {
+    protected AndroidKeyPairGenerator(@KeyStoreKeyProperties.AlgorithmEnum String algorithm) {
         mAlgorithm = algorithm;
     }
 
-    public String getAlgorithm() {
+    public @KeyStoreKeyProperties.AlgorithmEnum String getAlgorithm() {
         return mAlgorithm;
     }
 
@@ -197,7 +197,7 @@
         return certGen.generate(privateKey);
     }
 
-    private String getKeyAlgorithm(KeyPairGeneratorSpec spec) {
+    private @KeyStoreKeyProperties.AlgorithmEnum String getKeyAlgorithm(KeyPairGeneratorSpec spec) {
         String result = spec.getKeyType();
         if (result != null) {
             return result;
@@ -248,10 +248,11 @@
         }
     }
 
-    private static String getDefaultSignatureAlgorithmForKeyAlgorithm(String algorithm) {
-        if ("RSA".equalsIgnoreCase(algorithm)) {
+    private static String getDefaultSignatureAlgorithmForKeyAlgorithm(
+            @KeyStoreKeyProperties.AlgorithmEnum String algorithm) {
+        if (KeyStoreKeyProperties.Algorithm.RSA.equalsIgnoreCase(algorithm)) {
             return "sha256WithRSA";
-        } else if ("EC".equalsIgnoreCase(algorithm)) {
+        } else if (KeyStoreKeyProperties.Algorithm.EC.equalsIgnoreCase(algorithm)) {
             return "sha256WithECDSA";
         } else {
             throw new IllegalArgumentException("Unsupported key type " + algorithm);
@@ -287,7 +288,7 @@
         }
 
         KeyPairGeneratorSpec spec = (KeyPairGeneratorSpec) params;
-        String keyAlgorithm = getKeyAlgorithm(spec);
+        @KeyStoreKeyProperties.AlgorithmEnum String keyAlgorithm = getKeyAlgorithm(spec);
         int keyType = KeyStore.getKeyTypeForAlgorithm(keyAlgorithm);
         if (keyType == -1) {
             throw new InvalidAlgorithmParameterException(
diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java
index 72cb062..e82ff6a 100644
--- a/keystore/java/android/security/AndroidKeyStore.java
+++ b/keystore/java/android/security/AndroidKeyStore.java
@@ -128,10 +128,11 @@
                 keymasterDigest = keymasterDigests.get(0);
             }
 
-            String keyAlgorithmString;
+            @KeyStoreKeyProperties.AlgorithmEnum String keyAlgorithmString;
             try {
-                keyAlgorithmString = KeymasterUtils.getJcaSecretKeyAlgorithm(
-                        keymasterAlgorithm, keymasterDigest);
+                keyAlgorithmString =
+                        KeyStoreKeyProperties.Algorithm.fromKeymasterSecretKeyAlgorithm(
+                                keymasterAlgorithm, keymasterDigest);
             } catch (IllegalArgumentException e) {
                 throw (UnrecoverableKeyException)
                         new UnrecoverableKeyException("Unsupported secret key type").initCause(e);
@@ -451,10 +452,10 @@
         int keymasterAlgorithm;
         int keymasterDigest;
         try {
-            keymasterAlgorithm = KeymasterUtils.getKeymasterAlgorithmFromJcaSecretKeyAlgorithm(
+            keymasterAlgorithm = KeyStoreKeyProperties.Algorithm.toKeymasterSecretKeyAlgorithm(
                     keyAlgorithmString);
             keymasterDigest =
-                    KeymasterUtils.getKeymasterDigestfromJcaSecretKeyAlgorithm(keyAlgorithmString);
+                    KeyStoreKeyProperties.Algorithm.toKeymasterDigest(keyAlgorithmString);
         } catch (IllegalArgumentException e) {
             throw new KeyStoreException("Unsupported secret key algorithm: " + keyAlgorithmString);
         }
@@ -465,8 +466,7 @@
         int[] keymasterDigests;
         if (params.isDigestsSpecified()) {
             // Digest(s) specified in parameters
-            keymasterDigests =
-                    KeymasterUtils.getKeymasterDigestsFromJcaDigestAlgorithms(params.getDigests());
+            keymasterDigests = KeyStoreKeyProperties.Digest.allToKeymaster(params.getDigests());
             if (keymasterDigest != -1) {
                 // Digest also specified in the JCA key algorithm name.
                 if (!com.android.internal.util.ArrayUtils.contains(
@@ -494,8 +494,8 @@
         }
 
         @KeyStoreKeyProperties.PurposeEnum int purposes = params.getPurposes();
-        int[] keymasterBlockModes = KeymasterUtils.getKeymasterBlockModesFromJcaBlockModes(
-                params.getBlockModes());
+        int[] keymasterBlockModes =
+                KeyStoreKeyProperties.BlockMode.allToKeymaster(params.getBlockModes());
         if (((purposes & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0)
                 && (params.isRandomizedEncryptionRequired())) {
             for (int keymasterBlockMode : keymasterBlockModes) {
@@ -503,8 +503,7 @@
                     throw new KeyStoreException(
                             "Randomized encryption (IND-CPA) required but may be violated by block"
                             + " mode: "
-                            + KeymasterUtils.getJcaBlockModeFromKeymasterBlockMode(
-                                    keymasterBlockMode)
+                            + KeyStoreKeyProperties.BlockMode.fromKeymaster(keymasterBlockMode)
                             + ". See KeyStoreParameter documentation.");
                 }
             }
@@ -513,11 +512,11 @@
             args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
         }
         args.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes);
-        int[] keymasterPaddings = ArrayUtils.concat(
-                KeymasterUtils.getKeymasterPaddingsFromJcaEncryptionPaddings(
-                        params.getEncryptionPaddings()),
-                KeymasterUtils.getKeymasterPaddingsFromJcaSignaturePaddings(
-                        params.getSignaturePaddings()));
+        if (params.getSignaturePaddings().length > 0) {
+            throw new KeyStoreException("Signature paddings not supported for symmetric keys");
+        }
+        int[] keymasterPaddings = KeyStoreKeyProperties.EncryptionPadding.allToKeymaster(
+                params.getEncryptionPaddings());
         args.addInts(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings);
         KeymasterUtils.addUserAuthArgs(args,
                 params.getContext(),
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index e9c24dd..8e27dc3 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -262,7 +262,8 @@
      *     unavailable.
      */
     public static void choosePrivateKeyAlias(Activity activity, KeyChainAliasCallback response,
-            String[] keyTypes, Principal[] issuers, String host, int port, String alias) {
+            @KeyStoreKeyProperties.AlgorithmEnum String[] keyTypes, Principal[] issuers,
+            String host, int port, String alias) {
         choosePrivateKeyAlias(activity, response, keyTypes, issuers, host, port, null, alias);
     }
 
@@ -306,9 +307,8 @@
      *     unavailable.
      */
     public static void choosePrivateKeyAlias(Activity activity, KeyChainAliasCallback response,
-                                             String[] keyTypes, Principal[] issuers,
-                                             String host, int port, String url,
-                                             String alias) {
+            @KeyStoreKeyProperties.AlgorithmEnum String[] keyTypes, Principal[] issuers,
+            String host, int port, String url, String alias) {
         /*
          * TODO currently keyTypes, issuers are unused. They are meant
          * to follow the semantics and purpose of X509KeyManager
@@ -431,9 +431,11 @@
      * specific {@code PrivateKey} type indicated by {@code algorithm} (e.g.,
      * "RSA").
      */
-    public static boolean isKeyAlgorithmSupported(String algorithm) {
+    public static boolean isKeyAlgorithmSupported(
+            @KeyStoreKeyProperties.AlgorithmEnum String algorithm) {
         final String algUpper = algorithm.toUpperCase(Locale.US);
-        return "EC".equals(algUpper) || "RSA".equals(algUpper);
+        return KeyStoreKeyProperties.Algorithm.EC.equals(algUpper)
+                || KeyStoreKeyProperties.Algorithm.RSA.equals(algUpper);
     }
 
     /**
@@ -443,7 +445,8 @@
      * hardware support that can be used to bind keys to the device in a way
      * that makes it non-exportable.
      */
-    public static boolean isBoundKeyAlgorithm(String algorithm) {
+    public static boolean isBoundKeyAlgorithm(
+            @KeyStoreKeyProperties.AlgorithmEnum String algorithm) {
         if (!isKeyAlgorithmSupported(algorithm)) {
             return false;
         }
diff --git a/keystore/java/android/security/KeyGeneratorSpec.java b/keystore/java/android/security/KeyGeneratorSpec.java
index 8f135a6..729646d 100644
--- a/keystore/java/android/security/KeyGeneratorSpec.java
+++ b/keystore/java/android/security/KeyGeneratorSpec.java
@@ -48,8 +48,8 @@
     private final Date mKeyValidityForOriginationEnd;
     private final Date mKeyValidityForConsumptionEnd;
     private final @KeyStoreKeyProperties.PurposeEnum int mPurposes;
-    private final String[] mEncryptionPaddings;
-    private final String[] mBlockModes;
+    private final @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
+    private final @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
     private final boolean mRandomizedEncryptionRequired;
     private final boolean mUserAuthenticationRequired;
     private final int mUserAuthenticationValidityDurationSeconds;
@@ -63,8 +63,8 @@
             Date keyValidityForOriginationEnd,
             Date keyValidityForConsumptionEnd,
             @KeyStoreKeyProperties.PurposeEnum int purposes,
-            String[] encryptionPaddings,
-            String[] blockModes,
+            @KeyStoreKeyProperties.EncryptionPaddingEnum String[] encryptionPaddings,
+            @KeyStoreKeyProperties.BlockModeEnum String[] blockModes,
             boolean randomizedEncryptionRequired,
             boolean userAuthenticationRequired,
             int userAuthenticationValidityDurationSeconds) {
@@ -160,14 +160,14 @@
     /**
      * Gets the set of padding schemes with which the key can be used when encrypting/decrypting.
      */
-    public String[] getEncryptionPaddings() {
+    public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() {
         return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings);
     }
 
     /**
      * Gets the set of block modes with which the key can be used.
      */
-    public String[] getBlockModes() {
+    public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() {
         return ArrayUtils.cloneIfNotEmpty(mBlockModes);
     }
 
@@ -195,10 +195,12 @@
 
     /**
      * Gets the duration of time (seconds) for which this key can be used after the user is
-     * successfully authenticated.
+     * successfully authenticated. This has effect only if user authentication is required.
      *
-     * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication
-     *         is required for every use of the key.
+     * @return duration in seconds or {@code -1} if authentication is required for every use of the
+     *         key.
+     *
+     * @see #isUserAuthenticationRequired()
      */
     public int getUserAuthenticationValidityDurationSeconds() {
         return mUserAuthenticationValidityDurationSeconds;
@@ -220,8 +222,8 @@
         private Date mKeyValidityForOriginationEnd;
         private Date mKeyValidityForConsumptionEnd;
         private @KeyStoreKeyProperties.PurposeEnum int mPurposes;
-        private String[] mEncryptionPaddings;
-        private String[] mBlockModes;
+        private @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
+        private @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
         private boolean mRandomizedEncryptionRequired = true;
         private boolean mUserAuthenticationRequired;
         private int mUserAuthenticationValidityDurationSeconds = -1;
@@ -346,7 +348,8 @@
          *
          * <p>This must be specified for keys which are used for encryption/decryption.
          */
-        public Builder setEncryptionPaddings(String... paddings) {
+        public Builder setEncryptionPaddings(
+                @KeyStoreKeyProperties.EncryptionPaddingEnum String... paddings) {
             mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings);
             return this;
         }
@@ -357,7 +360,7 @@
          *
          * <p>This must be specified for encryption/decryption keys.
          */
-        public Builder setBlockModes(String... blockModes) {
+        public Builder setBlockModes(@KeyStoreKeyProperties.BlockModeEnum String... blockModes) {
             mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes);
             return this;
         }
@@ -425,7 +428,7 @@
          *
          * <p>By default, the user needs to authenticate for every use of the key.
          *
-         * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for
+         * @param seconds duration in seconds or {@code -1} if the user needs to authenticate for
          *        every use of the key.
          *
          * @see #setUserAuthenticationRequired(boolean)
diff --git a/keystore/java/android/security/KeyPairGeneratorSpec.java b/keystore/java/android/security/KeyPairGeneratorSpec.java
index d6d3789..25c61fd 100644
--- a/keystore/java/android/security/KeyPairGeneratorSpec.java
+++ b/keystore/java/android/security/KeyPairGeneratorSpec.java
@@ -85,13 +85,13 @@
 
     private final @KeyStoreKeyProperties.PurposeEnum int mPurposes;
 
-    private final String[] mDigests;
+    private final @KeyStoreKeyProperties.DigestEnum String[] mDigests;
 
-    private final String[] mEncryptionPaddings;
+    private final @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
 
-    private final String[] mSignaturePaddings;
+    private final @KeyStoreKeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
 
-    private final String[] mBlockModes;
+    private final @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
 
     private final boolean mRandomizedEncryptionRequired;
 
@@ -138,10 +138,10 @@
             Date keyValidityForOriginationEnd,
             Date keyValidityForConsumptionEnd,
             @KeyStoreKeyProperties.PurposeEnum int purposes,
-            String[] digests,
-            String[] encryptionPaddings,
-            String[] signaturePaddings,
-            String[] blockModes,
+            @KeyStoreKeyProperties.DigestEnum String[] digests,
+            @KeyStoreKeyProperties.EncryptionPaddingEnum String[] encryptionPaddings,
+            @KeyStoreKeyProperties.SignaturePaddingEnum String[] signaturePaddings,
+            @KeyStoreKeyProperties.BlockModeEnum String[] blockModes,
             boolean randomizedEncryptionRequired,
             boolean userAuthenticationRequired,
             int userAuthenticationValidityDurationSeconds) {
@@ -246,7 +246,7 @@
     /**
      * Returns the key type (e.g., "EC", "RSA") specified by this parameter.
      */
-    public String getKeyType() {
+    public @KeyStoreKeyProperties.AlgorithmEnum String getKeyType() {
         return mKeyType;
     }
 
@@ -352,28 +352,28 @@
     /**
      * Gets the set of digest algorithms with which the key can be used.
      */
-    public String[] getDigests() {
+    public @KeyStoreKeyProperties.DigestEnum String[] getDigests() {
         return ArrayUtils.cloneIfNotEmpty(mDigests);
     }
 
     /**
      * Gets the set of padding schemes with which the key can be used when encrypting/decrypting.
      */
-    public String[] getEncryptionPaddings() {
+    public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() {
         return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings);
     }
 
     /**
      * Gets the set of padding schemes with which the key can be used when signing/verifying.
      */
-    public String[] getSignaturePaddings() {
+    public @KeyStoreKeyProperties.SignaturePaddingEnum String[] getSignaturePaddings() {
         return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings);
     }
 
     /**
      * Gets the set of block modes with which the key can be used.
      */
-    public String[] getBlockModes() {
+    public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() {
         return ArrayUtils.cloneIfNotEmpty(mBlockModes);
     }
 
@@ -403,14 +403,14 @@
     }
 
     /**
-     * Gets the duration of time (seconds) for which the private key can be used after the user
-     * is successfully authenticated.
+     * Gets the duration of time (seconds) for which this key can be used after the user is
+     * successfully authenticated. This has effect only if user authentication is required.
      *
      * <p>This restriction applies only to private key operations. Public key operations are not
      * restricted.
      *
-     * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication
-     *         is required for every use of the key.
+     * @return duration in seconds or {@code -1} if authentication is required for every use of the
+     *         key.
      *
      * @see #isUserAuthenticationRequired()
      */
@@ -468,13 +468,13 @@
 
         private @KeyStoreKeyProperties.PurposeEnum int mPurposes;
 
-        private String[] mDigests;
+        private @KeyStoreKeyProperties.DigestEnum String[] mDigests;
 
-        private String[] mEncryptionPaddings;
+        private @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
 
-        private String[] mSignaturePaddings;
+        private @KeyStoreKeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
 
-        private String[] mBlockModes;
+        private @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
 
         private boolean mRandomizedEncryptionRequired = true;
 
@@ -511,7 +511,8 @@
         /**
          * Sets the key type (e.g., EC, RSA) of the keypair to be created.
          */
-        public Builder setKeyType(String keyType) throws NoSuchAlgorithmException {
+        public Builder setKeyType(@KeyStoreKeyProperties.AlgorithmEnum String keyType)
+                throws NoSuchAlgorithmException {
             if (keyType == null) {
                 throw new NullPointerException("keyType == null");
             } else {
@@ -628,6 +629,8 @@
          *
          * <p>By default, the key is valid at any instant.
          *
+         * <p><b>NOTE: This has currently no effect.
+         *
          * @see #setKeyValidityEnd(Date)
          */
         public Builder setKeyValidityStart(Date startDate) {
@@ -640,6 +643,8 @@
          *
          * <p>By default, the key is valid at any instant.
          *
+         * <p><b>NOTE: This has currently no effect.
+         *
          * @see #setKeyValidityStart(Date)
          * @see #setKeyValidityForConsumptionEnd(Date)
          * @see #setKeyValidityForOriginationEnd(Date)
@@ -655,6 +660,8 @@
          *
          * <p>By default, the key is valid at any instant.
          *
+         * <p><b>NOTE: This has currently no effect.
+         *
          * @see #setKeyValidityForConsumptionEnd(Date)
          */
         public Builder setKeyValidityForOriginationEnd(Date endDate) {
@@ -668,6 +675,8 @@
          *
          * <p>By default, the key is valid at any instant.
          *
+         * <p><b>NOTE: This has currently no effect.
+         *
          * @see #setKeyValidityForOriginationEnd(Date)
          */
         public Builder setKeyValidityForConsumptionEnd(Date endDate) {
@@ -679,6 +688,8 @@
          * Sets the set of purposes for which the key can be used.
          *
          * <p>This must be specified for all keys. There is no default.
+         *
+         * <p><b>NOTE: This has currently no effect.
          */
         public Builder setPurposes(@KeyStoreKeyProperties.PurposeEnum int purposes) {
             mPurposes = purposes;
@@ -690,8 +701,10 @@
          * to use the key with any other digest will be rejected.
          *
          * <p>This must be specified for keys which are used for signing/verification.
+         *
+         * <p><b>NOTE: This has currently no effect.
          */
-        public Builder setDigests(String... digests) {
+        public Builder setDigests(@KeyStoreKeyProperties.DigestEnum String... digests) {
             mDigests = ArrayUtils.cloneIfNotEmpty(digests);
             return this;
         }
@@ -702,8 +715,11 @@
          * rejected.
          *
          * <p>This must be specified for keys which are used for encryption/decryption.
+         *
+         * <p><b>NOTE: This has currently no effect.
          */
-        public Builder setEncryptionPaddings(String... paddings) {
+        public Builder setEncryptionPaddings(
+                @KeyStoreKeyProperties.EncryptionPaddingEnum String... paddings) {
             mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings);
             return this;
         }
@@ -714,8 +730,11 @@
          * rejected.
          *
          * <p>This must be specified for RSA keys which are used for signing/verification.
+         *
+         * <p><b>NOTE: This has currently no effect.
          */
-        public Builder setSignaturePaddings(String... paddings) {
+        public Builder setSignaturePaddings(
+                @KeyStoreKeyProperties.SignaturePaddingEnum String... paddings) {
             mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(paddings);
             return this;
         }
@@ -725,8 +744,10 @@
          * Attempts to use the key with any other block modes will be rejected.
          *
          * <p>This must be specified for encryption/decryption keys.
+         *
+         * <p><b>NOTE: This has currently no effect.
          */
-        public Builder setBlockModes(String... blockModes) {
+        public Builder setBlockModes(@KeyStoreKeyProperties.BlockModeEnum String... blockModes) {
             mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes);
             return this;
         }
@@ -750,6 +771,8 @@
          * <li>If you are using RSA encryption without padding, consider switching to padding
          * schemes which offer {@code IND-CPA}, such as PKCS#1 or OAEP.</li>
          * </ul>
+         *
+         * <p><b>NOTE: This has currently no effect.
          */
         public Builder setRandomizedEncryptionRequired(boolean required) {
             mRandomizedEncryptionRequired = required;
@@ -772,6 +795,8 @@
          * <p>This restriction applies only to private key operations. Public key operations are not
          * restricted.
          *
+         * <p><b>NOTE: This has currently no effect.
+         *
          * @see #setUserAuthenticationValidityDurationSeconds(int)
          */
         public Builder setUserAuthenticationRequired(boolean required) {
@@ -788,7 +813,9 @@
          * <p>This restriction applies only to private key operations. Public key operations are not
          * restricted.
          *
-         * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for
+         * <p><b>NOTE: This has currently no effect.
+         *
+         * @param seconds duration in seconds or {@code -1} if the user needs to authenticate for
          *        every use of the key.
          *
          * @see #setUserAuthenticationRequired(boolean)
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 82d328b..304d277 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -115,10 +115,10 @@
         return mToken;
     }
 
-    static int getKeyTypeForAlgorithm(String keyType) {
-        if ("RSA".equalsIgnoreCase(keyType)) {
+    static int getKeyTypeForAlgorithm(@KeyStoreKeyProperties.AlgorithmEnum String keyType) {
+        if (KeyStoreKeyProperties.Algorithm.RSA.equalsIgnoreCase(keyType)) {
             return NativeConstants.EVP_PKEY_RSA;
-        } else if ("EC".equalsIgnoreCase(keyType)) {
+        } else if (KeyStoreKeyProperties.Algorithm.EC.equalsIgnoreCase(keyType)) {
             return NativeConstants.EVP_PKEY_EC;
         } else {
             return -1;
diff --git a/keystore/java/android/security/KeyStoreCipherSpi.java b/keystore/java/android/security/KeyStoreCipherSpi.java
index 094aa75..bd601bc 100644
--- a/keystore/java/android/security/KeyStoreCipherSpi.java
+++ b/keystore/java/android/security/KeyStoreCipherSpi.java
@@ -27,6 +27,7 @@
 import java.security.InvalidKeyException;
 import java.security.Key;
 import java.security.NoSuchAlgorithmException;
+import java.security.ProviderException;
 import java.security.SecureRandom;
 import java.security.spec.AlgorithmParameterSpec;
 import java.security.spec.InvalidParameterSpecException;
@@ -315,15 +316,15 @@
             } else if (e instanceof InvalidAlgorithmParameterException) {
                 throw (InvalidAlgorithmParameterException) e;
             } else {
-                throw new RuntimeException("Unexpected exception type", e);
+                throw new ProviderException("Unexpected exception type", e);
             }
         }
 
         if (mOperationToken == null) {
-            throw new IllegalStateException("Keystore returned null operation token");
+            throw new ProviderException("Keystore returned null operation token");
         }
         if (mOperationHandle == 0) {
-            throw new IllegalStateException("Keystore returned invalid operation handle");
+            throw new ProviderException("Keystore returned invalid operation handle");
         }
 
         loadAlgorithmSpecificParametersFromBeginResult(keymasterOutputArgs);
@@ -494,13 +495,14 @@
         }
         if ((mIv != null) && (mIv.length > 0)) {
             try {
-                AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
+                AlgorithmParameters params =
+                        AlgorithmParameters.getInstance(KeyStoreKeyProperties.Algorithm.AES);
                 params.init(new IvParameterSpec(mIv));
                 return params;
             } catch (NoSuchAlgorithmException e) {
-                throw new RuntimeException("Failed to obtain AES AlgorithmParameters", e);
+                throw new ProviderException("Failed to obtain AES AlgorithmParameters", e);
             } catch (InvalidParameterSpecException e) {
-                throw new RuntimeException(
+                throw new ProviderException(
                         "Failed to initialize AES AlgorithmParameters with an IV", e);
             }
         }
@@ -633,10 +635,9 @@
                 if ((mIv == null) && (mEncrypting)) {
                     // IV was not provided by the caller and thus will be generated by keymaster.
                     // Mix in some additional entropy from the provided SecureRandom.
-                    if (mRng != null) {
-                        mAdditionalEntropyForBegin = new byte[mBlockSizeBytes];
-                        mRng.nextBytes(mAdditionalEntropyForBegin);
-                    }
+                    mAdditionalEntropyForBegin =
+                            KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
+                                    mRng, mBlockSizeBytes);
                 }
             }
         }
@@ -668,11 +669,11 @@
             if (mIv == null) {
                 mIv = returnedIv;
             } else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) {
-                throw new IllegalStateException("IV in use differs from provided IV");
+                throw new ProviderException("IV in use differs from provided IV");
             }
         } else {
             if (returnedIv != null) {
-                throw new IllegalStateException(
+                throw new ProviderException(
                         "IV in use despite IV not being used by this transformation");
             }
         }
diff --git a/keystore/java/android/security/KeyStoreConnectException.java b/keystore/java/android/security/KeyStoreConnectException.java
index 1aa3aec..885f1f7 100644
--- a/keystore/java/android/security/KeyStoreConnectException.java
+++ b/keystore/java/android/security/KeyStoreConnectException.java
@@ -16,12 +16,14 @@
 
 package android.security;
 
+import java.security.ProviderException;
+
 /**
  * Indicates a communications error with keystore service.
  *
  * @hide
  */
-public class KeyStoreConnectException extends IllegalStateException {
+public class KeyStoreConnectException extends ProviderException {
     public KeyStoreConnectException() {
         super("Failed to communicate with keystore service");
     }
diff --git a/keystore/java/android/security/KeyStoreCryptoOperationUtils.java b/keystore/java/android/security/KeyStoreCryptoOperationUtils.java
index e5933ad..311278b 100644
--- a/keystore/java/android/security/KeyStoreCryptoOperationUtils.java
+++ b/keystore/java/android/security/KeyStoreCryptoOperationUtils.java
@@ -21,6 +21,7 @@
 import java.security.GeneralSecurityException;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
+import java.security.SecureRandom;
 
 /**
  * Assorted utility methods for implementing crypto operations on top of KeyStore.
@@ -28,6 +29,9 @@
  * @hide
  */
 abstract class KeyStoreCryptoOperationUtils {
+
+    private static volatile SecureRandom sRng;
+
     private KeyStoreCryptoOperationUtils() {}
 
     /**
@@ -81,4 +85,28 @@
         // General cases
         return getInvalidKeyExceptionForInit(keyStore, key, beginOpResultCode);
     }
+
+    /**
+     * Returns the requested number of random bytes to mix into keystore/keymaster RNG.
+     *
+     * @param rng RNG from which to obtain the random bytes or {@code null} for the platform-default
+     *        RNG.
+     */
+    static byte[] getRandomBytesToMixIntoKeystoreRng(SecureRandom rng, int sizeBytes) {
+        if (rng == null) {
+            rng = getRng();
+        }
+        byte[] result = new byte[sizeBytes];
+        rng.nextBytes(result);
+        return result;
+    }
+
+    private static SecureRandom getRng() {
+        // IMPLEMENTATION NOTE: It's OK to share a SecureRandom instance because SecureRandom is
+        // required to be thread-safe.
+        if (sRng == null) {
+            sRng = new SecureRandom();
+        }
+        return sRng;
+    }
 }
diff --git a/keystore/java/android/security/KeyStoreHmacSpi.java b/keystore/java/android/security/KeyStoreHmacSpi.java
index 0dbe788..5089a25 100644
--- a/keystore/java/android/security/KeyStoreHmacSpi.java
+++ b/keystore/java/android/security/KeyStoreHmacSpi.java
@@ -24,6 +24,7 @@
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
 import java.security.Key;
+import java.security.ProviderException;
 import java.security.spec.AlgorithmParameterSpec;
 
 import javax.crypto.MacSpi;
@@ -185,10 +186,10 @@
         }
 
         if (mOperationToken == null) {
-            throw new IllegalStateException("Keystore returned null operation token");
+            throw new ProviderException("Keystore returned null operation token");
         }
         if (mOperationHandle == 0) {
-            throw new IllegalStateException("Keystore returned invalid operation handle");
+            throw new ProviderException("Keystore returned invalid operation handle");
         }
 
         mChunkedStreamer = new KeyStoreCryptoOperationChunkedStreamer(
@@ -206,17 +207,17 @@
         try {
             ensureKeystoreOperationInitialized();
         } catch (InvalidKeyException e) {
-            throw new IllegalStateException("Failed to reinitialize MAC", e);
+            throw new ProviderException("Failed to reinitialize MAC", e);
         }
 
         byte[] output;
         try {
             output = mChunkedStreamer.update(input, offset, len);
         } catch (KeyStoreException e) {
-            throw new IllegalStateException("Keystore operation failed", e);
+            throw new ProviderException("Keystore operation failed", e);
         }
         if ((output != null) && (output.length != 0)) {
-            throw new IllegalStateException("Update operation unexpectedly produced output");
+            throw new ProviderException("Update operation unexpectedly produced output");
         }
     }
 
@@ -225,14 +226,14 @@
         try {
             ensureKeystoreOperationInitialized();
         } catch (InvalidKeyException e) {
-            throw new IllegalStateException("Failed to reinitialize MAC", e);
+            throw new ProviderException("Failed to reinitialize MAC", e);
         }
 
         byte[] result;
         try {
             result = mChunkedStreamer.doFinal(null, 0, 0);
         } catch (KeyStoreException e) {
-            throw new IllegalStateException("Keystore operation failed", e);
+            throw new ProviderException("Keystore operation failed", e);
         }
 
         resetWhilePreservingInitState();
diff --git a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
index 68b5751..4b914c2 100644
--- a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
@@ -21,6 +21,7 @@
 import android.security.keymaster.KeymasterDefs;
 
 import java.security.InvalidAlgorithmParameterException;
+import java.security.ProviderException;
 import java.security.SecureRandom;
 import java.security.spec.AlgorithmParameterSpec;
 import java.util.Date;
@@ -39,6 +40,17 @@
         public AES() {
             super(KeymasterDefs.KM_ALGORITHM_AES, 128);
         }
+
+        @Override
+        protected void engineInit(AlgorithmParameterSpec params, SecureRandom random)
+                throws InvalidAlgorithmParameterException {
+            super.engineInit(params, random);
+            if ((mKeySizeBits != 128) && (mKeySizeBits != 192) && (mKeySizeBits != 256)) {
+                throw new InvalidAlgorithmParameterException(
+                        "Unsupported key size: " + mKeySizeBits
+                        + ". Supported: 128, 192, 256.");
+            }
+        }
     }
 
     protected static abstract class HmacBase extends KeyStoreKeyGeneratorSpi {
@@ -87,6 +99,11 @@
     private KeyGeneratorSpec mSpec;
     private SecureRandom mRng;
 
+    protected int mKeySizeBits;
+    private int[] mKeymasterPurposes;
+    private int[] mKeymasterBlockModes;
+    private int[] mKeymasterPaddings;
+
     protected KeyStoreKeyGeneratorSpi(
             int keymasterAlgorithm,
             int defaultKeySizeBits) {
@@ -100,6 +117,97 @@
         mKeymasterAlgorithm = keymasterAlgorithm;
         mKeymasterDigest = keymasterDigest;
         mDefaultKeySizeBits = defaultKeySizeBits;
+        if (mDefaultKeySizeBits <= 0) {
+            throw new IllegalArgumentException("Default key size must be positive");
+        }
+
+        if ((mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) && (mKeymasterDigest == -1)) {
+            throw new IllegalArgumentException(
+                    "Digest algorithm must be specified for HMAC key");
+        }
+    }
+
+    @Override
+    protected void engineInit(SecureRandom random) {
+        throw new UnsupportedOperationException("Cannot initialize without an "
+                + KeyGeneratorSpec.class.getName() + " parameter");
+    }
+
+    @Override
+    protected void engineInit(int keySize, SecureRandom random) {
+        throw new UnsupportedOperationException("Cannot initialize without a "
+                + KeyGeneratorSpec.class.getName() + " parameter");
+    }
+
+    @Override
+    protected void engineInit(AlgorithmParameterSpec params, SecureRandom random)
+            throws InvalidAlgorithmParameterException {
+        resetAll();
+
+        boolean success = false;
+        try {
+            if ((params == null) || (!(params instanceof KeyGeneratorSpec))) {
+                throw new InvalidAlgorithmParameterException("Cannot initialize without an "
+                        + KeyGeneratorSpec.class.getName() + " parameter");
+            }
+            KeyGeneratorSpec spec = (KeyGeneratorSpec) params;
+            if (spec.getKeystoreAlias() == null) {
+                throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided");
+            }
+
+            mRng = random;
+            mSpec = spec;
+
+            mKeySizeBits = (spec.getKeySize() != -1) ? spec.getKeySize() : mDefaultKeySizeBits;
+            if (mKeySizeBits <= 0) {
+                throw new InvalidAlgorithmParameterException(
+                        "Key size must be positive: " + mKeySizeBits);
+            } else if ((mKeySizeBits % 8) != 0) {
+                throw new InvalidAlgorithmParameterException(
+                        "Key size in must be a multiple of 8: " + mKeySizeBits);
+            }
+
+            try {
+                mKeymasterPurposes =
+                        KeyStoreKeyProperties.Purpose.allToKeymaster(spec.getPurposes());
+                mKeymasterPaddings = KeyStoreKeyProperties.EncryptionPadding.allToKeymaster(
+                        spec.getEncryptionPaddings());
+                mKeymasterBlockModes =
+                        KeyStoreKeyProperties.BlockMode.allToKeymaster(spec.getBlockModes());
+                if (((spec.getPurposes() & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0)
+                        && (spec.isRandomizedEncryptionRequired())) {
+                    for (int keymasterBlockMode : mKeymasterBlockModes) {
+                        if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatible(
+                                keymasterBlockMode)) {
+                            throw new InvalidAlgorithmParameterException(
+                                    "Randomized encryption (IND-CPA) required but may be violated"
+                                    + " by block mode: "
+                                    + KeyStoreKeyProperties.BlockMode.fromKeymaster(
+                                            keymasterBlockMode)
+                                    + ". See " + KeyGeneratorSpec.class.getName()
+                                    + " documentation.");
+                        }
+                    }
+                }
+            } catch (IllegalArgumentException e) {
+                throw new InvalidAlgorithmParameterException(e);
+            }
+
+            success = true;
+        } finally {
+            if (!success) {
+                resetAll();
+            }
+        }
+    }
+
+    private void resetAll() {
+        mSpec = null;
+        mRng = null;
+        mKeySizeBits = -1;
+        mKeymasterPurposes = null;
+        mKeymasterPaddings = null;
+        mKeymasterBlockModes = null;
     }
 
     @Override
@@ -117,43 +225,14 @@
         }
 
         KeymasterArguments args = new KeymasterArguments();
+        args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits);
         args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm);
         if (mKeymasterDigest != -1) {
             args.addInt(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
         }
-        if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) {
-            if (mKeymasterDigest == -1) {
-                throw new IllegalStateException("Digest algorithm must be specified for HMAC key");
-            }
-        }
-        int keySizeBits = (spec.getKeySize() != -1) ? spec.getKeySize() : mDefaultKeySizeBits;
-        args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keySizeBits);
-        @KeyStoreKeyProperties.PurposeEnum int purposes = spec.getPurposes();
-        int[] keymasterBlockModes = KeymasterUtils.getKeymasterBlockModesFromJcaBlockModes(
-                spec.getBlockModes());
-        if (((purposes & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0)
-                && (spec.isRandomizedEncryptionRequired())) {
-            for (int keymasterBlockMode : keymasterBlockModes) {
-                if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatible(keymasterBlockMode)) {
-                    throw new IllegalStateException(
-                            "Randomized encryption (IND-CPA) required but may be violated by block"
-                            + " mode: "
-                            + KeymasterUtils.getJcaBlockModeFromKeymasterBlockMode(
-                                    keymasterBlockMode)
-                            + ". See KeyGeneratorSpec documentation.");
-                }
-            }
-        }
-
-        for (int keymasterPurpose :
-            KeyStoreKeyProperties.Purpose.allToKeymaster(purposes)) {
-            args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
-        }
-        args.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes);
-        args.addInts(
-                KeymasterDefs.KM_TAG_PADDING,
-                KeymasterUtils.getKeymasterPaddingsFromJcaEncryptionPaddings(
-                        spec.getEncryptionPaddings()));
+        args.addInts(KeymasterDefs.KM_TAG_PURPOSE, mKeymasterPurposes);
+        args.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockModes);
+        args.addInts(KeymasterDefs.KM_TAG_PADDING, mKeymasterPaddings);
         KeymasterUtils.addUserAuthArgs(args,
                 spec.getContext(),
                 spec.isUserAuthenticationRequired(),
@@ -168,57 +247,31 @@
                 (spec.getKeyValidityForConsumptionEnd() != null)
                 ? spec.getKeyValidityForConsumptionEnd() : new Date(Long.MAX_VALUE));
 
-        if (((purposes & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0)
+        if (((spec.getPurposes() & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0)
                 && (!spec.isRandomizedEncryptionRequired())) {
             // Permit caller-provided IV when encrypting with this key
             args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
         }
 
-        byte[] additionalEntropy = null;
-        SecureRandom rng = mRng;
-        if (rng != null) {
-            additionalEntropy = new byte[(keySizeBits + 7) / 8];
-            rng.nextBytes(additionalEntropy);
-        }
-
+        byte[] additionalEntropy =
+                KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
+                        mRng, (mKeySizeBits + 7) / 8);
         int flags = spec.getFlags();
         String keyAliasInKeystore = Credentials.USER_SECRET_KEY + spec.getKeystoreAlias();
+        KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
         int errorCode = mKeyStore.generateKey(
-                keyAliasInKeystore, args, additionalEntropy, flags, new KeyCharacteristics());
+                keyAliasInKeystore, args, additionalEntropy, flags, resultingKeyCharacteristics);
         if (errorCode != KeyStore.NO_ERROR) {
-            throw new IllegalStateException(
+            throw new ProviderException(
                     "Keystore operation failed", KeyStore.getKeyStoreException(errorCode));
         }
-        String keyAlgorithmJCA =
-                KeymasterUtils.getJcaSecretKeyAlgorithm(mKeymasterAlgorithm, mKeymasterDigest);
+        String keyAlgorithmJCA;
+        try {
+            keyAlgorithmJCA = KeyStoreKeyProperties.Algorithm.fromKeymasterSecretKeyAlgorithm(
+                    mKeymasterAlgorithm, mKeymasterDigest);
+        } catch (IllegalArgumentException e) {
+            throw new ProviderException("Failed to obtain JCA secret key algorithm name", e);
+        }
         return new KeyStoreSecretKey(keyAliasInKeystore, keyAlgorithmJCA);
     }
-
-    @Override
-    protected void engineInit(SecureRandom random) {
-        throw new UnsupportedOperationException("Cannot initialize without an "
-                + KeyGeneratorSpec.class.getName() + " parameter");
-    }
-
-    @Override
-    protected void engineInit(AlgorithmParameterSpec params, SecureRandom random)
-            throws InvalidAlgorithmParameterException {
-        if ((params == null) || (!(params instanceof KeyGeneratorSpec))) {
-            throw new InvalidAlgorithmParameterException("Cannot initialize without an "
-                    + KeyGeneratorSpec.class.getName() + " parameter");
-        }
-        KeyGeneratorSpec spec = (KeyGeneratorSpec) params;
-        if (spec.getKeystoreAlias() == null) {
-            throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided");
-        }
-
-        mSpec = spec;
-        mRng = random;
-    }
-
-    @Override
-    protected void engineInit(int keySize, SecureRandom random) {
-        throw new UnsupportedOperationException("Cannot initialize without a "
-                + KeyGeneratorSpec.class.getName() + " parameter");
-    }
 }
diff --git a/keystore/java/android/security/KeyStoreKeyProperties.java b/keystore/java/android/security/KeyStoreKeyProperties.java
index b85ec53..1c3e300 100644
--- a/keystore/java/android/security/KeyStoreKeyProperties.java
+++ b/keystore/java/android/security/KeyStoreKeyProperties.java
@@ -17,13 +17,23 @@
 package android.security;
 
 import android.annotation.IntDef;
+import android.annotation.StringDef;
 import android.security.keymaster.KeymasterDefs;
 
 import libcore.util.EmptyArray;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
 import java.util.Collection;
+import java.util.Locale;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.Mac;
+import javax.crypto.SecretKeyFactory;
 
 /**
  * Properties of {@code AndroidKeyStore} keys.
@@ -37,7 +47,7 @@
     public @interface PurposeEnum {}
 
     /**
-     * Purpose of key.
+     * Purposes of key.
      */
     public static abstract class Purpose {
         private Purpose() {}
@@ -122,6 +132,514 @@
     }
 
     @Retention(RetentionPolicy.SOURCE)
+    @StringDef({
+        Algorithm.RSA,
+        Algorithm.EC,
+        Algorithm.AES,
+        Algorithm.HMAC_SHA1,
+        Algorithm.HMAC_SHA224,
+        Algorithm.HMAC_SHA256,
+        Algorithm.HMAC_SHA384,
+        Algorithm.HMAC_SHA512,
+        })
+    public @interface AlgorithmEnum {}
+
+    /**
+     * Key algorithms.
+     *
+     * <p>These are standard names which can be used to obtain instances of {@link KeyGenerator},
+     * {@link KeyPairGenerator}, {@link Cipher} (as part of the transformation string), {@link Mac},
+     * {@link KeyFactory}, {@link SecretKeyFactory}. These are also the names used by
+     * {@link Key#getAlgorithm()}.
+     */
+    public static abstract class Algorithm {
+        private Algorithm() {}
+
+        /** Rivest Shamir Adleman (RSA) key. */
+        public static final String RSA = "RSA";
+
+        /** Elliptic Curve (EC) key. */
+        public static final String EC = "EC";
+
+        /** Advanced Encryption Standard (AES) key. */
+        public static final String AES = "AES";
+
+        /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-1 as the hash. */
+        public static final String HMAC_SHA1 = "HmacSHA1";
+
+        /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-224 as the hash. */
+        public static final String HMAC_SHA224 = "HmacSHA224";
+
+        /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-256 as the hash. */
+        public static final String HMAC_SHA256 = "HmacSHA256";
+
+        /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-384 as the hash. */
+        public static final String HMAC_SHA384 = "HmacSHA384";
+
+        /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-512 as the hash. */
+        public static final String HMAC_SHA512 = "HmacSHA512";
+
+        /**
+         * @hide
+         */
+        static int toKeymasterSecretKeyAlgorithm(@AlgorithmEnum String algorithm) {
+            if (AES.equalsIgnoreCase(algorithm)) {
+                return KeymasterDefs.KM_ALGORITHM_AES;
+            } else if (algorithm.toUpperCase(Locale.US).startsWith("HMAC")) {
+                return KeymasterDefs.KM_ALGORITHM_HMAC;
+            } else {
+                throw new IllegalArgumentException(
+                        "Unsupported secret key algorithm: " + algorithm);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        static @AlgorithmEnum String fromKeymasterSecretKeyAlgorithm(
+                int keymasterAlgorithm, int keymasterDigest) {
+            switch (keymasterAlgorithm) {
+                case KeymasterDefs.KM_ALGORITHM_AES:
+                    if (keymasterDigest != -1) {
+                        throw new IllegalArgumentException("Digest not supported for AES key: "
+                                + Digest.fromKeymaster(keymasterDigest));
+                    }
+                    return AES;
+                case KeymasterDefs.KM_ALGORITHM_HMAC:
+                    switch (keymasterDigest) {
+                        case KeymasterDefs.KM_DIGEST_SHA1:
+                            return HMAC_SHA1;
+                        case KeymasterDefs.KM_DIGEST_SHA_2_224:
+                            return HMAC_SHA224;
+                        case KeymasterDefs.KM_DIGEST_SHA_2_256:
+                            return HMAC_SHA256;
+                        case KeymasterDefs.KM_DIGEST_SHA_2_384:
+                            return HMAC_SHA384;
+                        case KeymasterDefs.KM_DIGEST_SHA_2_512:
+                            return HMAC_SHA512;
+                        default:
+                            throw new IllegalArgumentException("Unsupported HMAC digest: "
+                                    + Digest.fromKeymaster(keymasterDigest));
+                    }
+                default:
+                    throw new IllegalArgumentException(
+                            "Unsupported algorithm: " + keymasterAlgorithm);
+            }
+        }
+
+        /**
+         * @hide
+         *
+         * @return keymaster digest or {@code -1} if the algorithm does not involve a digest.
+         */
+        static int toKeymasterDigest(@AlgorithmEnum String algorithm) {
+            String algorithmUpper = algorithm.toUpperCase(Locale.US);
+            if (algorithmUpper.startsWith("HMAC")) {
+                String digestUpper = algorithmUpper.substring("HMAC".length());
+                switch (digestUpper) {
+                    case "SHA1":
+                        return KeymasterDefs.KM_DIGEST_SHA1;
+                    case "SHA224":
+                        return KeymasterDefs.KM_DIGEST_SHA_2_224;
+                    case "SHA256":
+                        return KeymasterDefs.KM_DIGEST_SHA_2_256;
+                    case "SHA384":
+                        return KeymasterDefs.KM_DIGEST_SHA_2_384;
+                    case "SHA512":
+                        return KeymasterDefs.KM_DIGEST_SHA_2_512;
+                    default:
+                        throw new IllegalArgumentException(
+                                "Unsupported HMAC digest: " + digestUpper);
+                }
+            } else {
+                return -1;
+            }
+        }
+    }
+
+    @Retention(RetentionPolicy.SOURCE)
+    @StringDef({
+        BlockMode.ECB,
+        BlockMode.CBC,
+        BlockMode.CTR,
+        BlockMode.GCM,
+        })
+    public @interface BlockModeEnum {}
+
+    /**
+     * Block modes that can be used when encrypting/decrypting using a key.
+     */
+    public static abstract class BlockMode {
+        private BlockMode() {}
+
+        /** Electronic Codebook (ECB) block mode. */
+        public static final String ECB = "ECB";
+
+        /** Cipher Block Chaining (CBC) block mode. */
+        public static final String CBC = "CBC";
+
+        /** Counter (CTR) block mode. */
+        public static final String CTR = "CTR";
+
+        /** Galois/Counter Mode (GCM) block mode. */
+        public static final String GCM = "GCM";
+
+        /**
+         * @hide
+         */
+        static int toKeymaster(@BlockModeEnum String blockMode) {
+            if (ECB.equalsIgnoreCase(blockMode)) {
+                return KeymasterDefs.KM_MODE_ECB;
+            } else if (CBC.equalsIgnoreCase(blockMode)) {
+                return KeymasterDefs.KM_MODE_CBC;
+            } else if (CTR.equalsIgnoreCase(blockMode)) {
+                return KeymasterDefs.KM_MODE_CTR;
+            } else if (GCM.equalsIgnoreCase(blockMode)) {
+                return KeymasterDefs.KM_MODE_GCM;
+            } else {
+                throw new IllegalArgumentException("Unsupported block mode: " + blockMode);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        static @BlockModeEnum String fromKeymaster(int blockMode) {
+            switch (blockMode) {
+                case KeymasterDefs.KM_MODE_ECB:
+                    return ECB;
+                case KeymasterDefs.KM_MODE_CBC:
+                    return CBC;
+                case KeymasterDefs.KM_MODE_CTR:
+                    return CTR;
+                case KeymasterDefs.KM_MODE_GCM:
+                    return GCM;
+                default:
+                    throw new IllegalArgumentException("Unsupported block mode: " + blockMode);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        static @BlockModeEnum String[] allFromKeymaster(Collection<Integer> blockModes) {
+            if ((blockModes == null) || (blockModes.isEmpty())) {
+                return EmptyArray.STRING;
+            }
+            @BlockModeEnum String[] result = new String[blockModes.size()];
+            int offset = 0;
+            for (int blockMode : blockModes) {
+                result[offset] = fromKeymaster(blockMode);
+                offset++;
+            }
+            return result;
+        }
+
+        /**
+         * @hide
+         */
+        static int[] allToKeymaster(@BlockModeEnum String[] blockModes) {
+            if ((blockModes == null) || (blockModes.length == 0)) {
+                return EmptyArray.INT;
+            }
+            int[] result = new int[blockModes.length];
+            for (int i = 0; i < blockModes.length; i++) {
+                result[i] = toKeymaster(blockModes[i]);
+            }
+            return result;
+        }
+    }
+
+    @Retention(RetentionPolicy.SOURCE)
+    @StringDef({
+        EncryptionPadding.NONE,
+        EncryptionPadding.PKCS7,
+        EncryptionPadding.RSA_PKCS1,
+        EncryptionPadding.RSA_OAEP,
+        })
+    public @interface EncryptionPaddingEnum {}
+
+    /**
+     * Padding schemes for encryption/decryption.
+     */
+    public static abstract class EncryptionPadding {
+        private EncryptionPadding() {}
+
+        /**
+         * No padding.
+         */
+        public static final String NONE = "NoPadding";
+
+        /**
+         * PKCS#7 padding.
+         */
+        public static final String PKCS7 = "PKCS7Padding";
+
+        /**
+         * RSA PKCS#1 v1.5 padding for encryption/decryption.
+         */
+        public static final String RSA_PKCS1 = "PKCS1Padding";
+
+        /**
+         * RSA Optimal Asymmetric Encryption Padding (OAEP).
+         */
+        public static final String RSA_OAEP = "OAEPPadding";
+
+        /**
+         * @hide
+         */
+        static int toKeymaster(@EncryptionPaddingEnum String padding) {
+            if (NONE.equalsIgnoreCase(padding)) {
+                return KeymasterDefs.KM_PAD_NONE;
+            } else if (PKCS7.equalsIgnoreCase(padding)) {
+                return KeymasterDefs.KM_PAD_PKCS7;
+            } else if (RSA_PKCS1.equalsIgnoreCase(padding)) {
+                return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT;
+            } else if (RSA_OAEP.equalsIgnoreCase(padding)) {
+                return KeymasterDefs.KM_PAD_RSA_OAEP;
+            } else {
+                throw new IllegalArgumentException(
+                        "Unsupported encryption padding scheme: " + padding);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        static @EncryptionPaddingEnum String fromKeymaster(int padding) {
+            switch (padding) {
+                case KeymasterDefs.KM_PAD_NONE:
+                    return NONE;
+                case KeymasterDefs.KM_PAD_PKCS7:
+                    return PKCS7;
+                case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT:
+                    return RSA_PKCS1;
+                case KeymasterDefs.KM_PAD_RSA_OAEP:
+                    return RSA_OAEP;
+                default:
+                    throw new IllegalArgumentException(
+                            "Unsupported encryption padding: " + padding);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        static int[] allToKeymaster(@EncryptionPaddingEnum String[] paddings) {
+            if ((paddings == null) || (paddings.length == 0)) {
+                return EmptyArray.INT;
+            }
+            int[] result = new int[paddings.length];
+            for (int i = 0; i < paddings.length; i++) {
+                result[i] = toKeymaster(paddings[i]);
+            }
+            return result;
+        }
+    }
+
+    @Retention(RetentionPolicy.SOURCE)
+    @StringDef({
+        SignaturePadding.RSA_PKCS1,
+        SignaturePadding.RSA_PSS,
+        })
+    public @interface SignaturePaddingEnum {}
+
+    /**
+     * Padding schemes for signing/verification.
+     */
+    public static abstract class SignaturePadding {
+        private SignaturePadding() {}
+
+        /**
+         * RSA PKCS#1 v1.5 padding for signatures.
+         */
+        public static final String RSA_PKCS1 = "PKCS1";
+
+        /**
+         * RSA PKCS#1 v2.1 Probabilistic Signature Scheme (PSS) padding.
+         */
+        public static final String RSA_PSS = "PSS";
+
+        /**
+         * @hide
+         */
+        static int toKeymaster(@SignaturePaddingEnum String padding) {
+            switch (padding.toUpperCase(Locale.US)) {
+                case RSA_PKCS1:
+                    return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN;
+                case RSA_PSS:
+                    return KeymasterDefs.KM_PAD_RSA_PSS;
+                default:
+                    throw new IllegalArgumentException(
+                            "Unsupported signature padding scheme: " + padding);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        static @SignaturePaddingEnum String fromKeymaster(int padding) {
+            switch (padding) {
+                case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN:
+                    return RSA_PKCS1;
+                case KeymasterDefs.KM_PAD_RSA_PSS:
+                    return RSA_PSS;
+                default:
+                    throw new IllegalArgumentException("Unsupported signature padding: " + padding);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        static int[] allToKeymaster(@SignaturePaddingEnum String[] paddings) {
+            if ((paddings == null) || (paddings.length == 0)) {
+                return EmptyArray.INT;
+            }
+            int[] result = new int[paddings.length];
+            for (int i = 0; i < paddings.length; i++) {
+                result[i] = toKeymaster(paddings[i]);
+            }
+            return result;
+        }
+    }
+
+    @Retention(RetentionPolicy.SOURCE)
+    @StringDef({
+        Digest.NONE,
+        Digest.MD5,
+        Digest.SHA1,
+        Digest.SHA224,
+        Digest.SHA256,
+        Digest.SHA384,
+        Digest.SHA512,
+        })
+    public @interface DigestEnum {}
+
+    /**
+     * Digests that can be used with a key when signing or generating Message Authentication
+     * Codes (MACs).
+     */
+    public static abstract class Digest {
+        private Digest() {}
+
+        /**
+         * No digest: sign/authenticate the raw message.
+         */
+        public static final String NONE = "NONE";
+
+        /**
+         * MD5 digest.
+         */
+        public static final String MD5 = "MD5";
+
+        /**
+         * SHA-1 digest.
+         */
+        public static final String SHA1 = "SHA-1";
+
+        /**
+         * SHA-2 224 (aka SHA-224) digest.
+         */
+        public static final String SHA224 = "SHA-224";
+
+        /**
+         * SHA-2 256 (aka SHA-256) digest.
+         */
+        public static final String SHA256 = "SHA-256";
+
+        /**
+         * SHA-2 384 (aka SHA-384) digest.
+         */
+        public static final String SHA384 = "SHA-384";
+
+        /**
+         * SHA-2 512 (aka SHA-512) digest.
+         */
+        public static final String SHA512 = "SHA-512";
+
+        /**
+         * @hide
+         */
+        static int toKeymaster(@DigestEnum String digest) {
+            switch (digest.toUpperCase(Locale.US)) {
+                case SHA1:
+                    return KeymasterDefs.KM_DIGEST_SHA1;
+                case SHA224:
+                    return KeymasterDefs.KM_DIGEST_SHA_2_224;
+                case SHA256:
+                    return KeymasterDefs.KM_DIGEST_SHA_2_256;
+                case SHA384:
+                    return KeymasterDefs.KM_DIGEST_SHA_2_384;
+                case SHA512:
+                    return KeymasterDefs.KM_DIGEST_SHA_2_512;
+                case NONE:
+                    return KeymasterDefs.KM_DIGEST_NONE;
+                case MD5:
+                    return KeymasterDefs.KM_DIGEST_MD5;
+                default:
+                    throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        static @DigestEnum String fromKeymaster(int digest) {
+            switch (digest) {
+                case KeymasterDefs.KM_DIGEST_NONE:
+                    return NONE;
+                case KeymasterDefs.KM_DIGEST_MD5:
+                    return MD5;
+                case KeymasterDefs.KM_DIGEST_SHA1:
+                    return SHA1;
+                case KeymasterDefs.KM_DIGEST_SHA_2_224:
+                    return SHA224;
+                case KeymasterDefs.KM_DIGEST_SHA_2_256:
+                    return SHA256;
+                case KeymasterDefs.KM_DIGEST_SHA_2_384:
+                    return SHA384;
+                case KeymasterDefs.KM_DIGEST_SHA_2_512:
+                    return SHA512;
+                default:
+                    throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        static @DigestEnum String[] allFromKeymaster(Collection<Integer> digests) {
+            if (digests.isEmpty()) {
+                return EmptyArray.STRING;
+            }
+            String[] result = new String[digests.size()];
+            int offset = 0;
+            for (int digest : digests) {
+                result[offset] = fromKeymaster(digest);
+                offset++;
+            }
+            return result;
+        }
+
+        /**
+         * @hide
+         */
+        static int[] allToKeymaster(@DigestEnum String[] digests) {
+            if ((digests == null) || (digests.length == 0)) {
+                return EmptyArray.INT;
+            }
+            int[] result = new int[digests.length];
+            int offset = 0;
+            for (@DigestEnum String digest : digests) {
+                result[offset] = toKeymaster(digest);
+                offset++;
+            }
+            return result;
+        }
+    }
+
+    @Retention(RetentionPolicy.SOURCE)
     @IntDef({Origin.GENERATED, Origin.IMPORTED, Origin.UNKNOWN})
     public @interface OriginEnum {}
 
diff --git a/keystore/java/android/security/KeyStoreKeySpec.java b/keystore/java/android/security/KeyStoreKeySpec.java
index 96d58d8..7533bdc 100644
--- a/keystore/java/android/security/KeyStoreKeySpec.java
+++ b/keystore/java/android/security/KeyStoreKeySpec.java
@@ -32,10 +32,10 @@
     private final Date mKeyValidityForOriginationEnd;
     private final Date mKeyValidityForConsumptionEnd;
     private final @KeyStoreKeyProperties.PurposeEnum int mPurposes;
-    private final String[] mEncryptionPaddings;
-    private final String[] mSignaturePaddings;
-    private final String[] mDigests;
-    private final String[] mBlockModes;
+    private final @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
+    private final @KeyStoreKeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
+    private final @KeyStoreKeyProperties.DigestEnum String[] mDigests;
+    private final @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
     private final boolean mUserAuthenticationRequired;
     private final int mUserAuthenticationValidityDurationSeconds;
     private final boolean mUserAuthenticationRequirementTeeEnforced;
@@ -51,10 +51,10 @@
             Date keyValidityForOriginationEnd,
             Date keyValidityForConsumptionEnd,
             @KeyStoreKeyProperties.PurposeEnum int purposes,
-            String[] encryptionPaddings,
-            String[] signaturePaddings,
-            String[] digests,
-            String[] blockModes,
+            @KeyStoreKeyProperties.EncryptionPaddingEnum String[] encryptionPaddings,
+            @KeyStoreKeyProperties.SignaturePaddingEnum String[] signaturePaddings,
+            @KeyStoreKeyProperties.DigestEnum String[] digests,
+            @KeyStoreKeyProperties.BlockModeEnum String[] blockModes,
             boolean userAuthenticationRequired,
             int userAuthenticationValidityDurationSeconds,
             boolean userAuthenticationRequirementTeeEnforced) {
@@ -143,28 +143,28 @@
     /**
      * Gets the set of block modes with which the key can be used.
      */
-    public String[] getBlockModes() {
+    public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() {
         return ArrayUtils.cloneIfNotEmpty(mBlockModes);
     }
 
     /**
      * Gets the set of padding modes with which the key can be used when encrypting/decrypting.
      */
-    public String[] getEncryptionPaddings() {
+    public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() {
         return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings);
     }
 
     /**
      * Gets the set of padding modes with which the key can be used when signing/verifying.
      */
-    public String[] getSignaturePaddings() {
+    public @KeyStoreKeyProperties.SignaturePaddingEnum String[] getSignaturePaddings() {
         return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings);
     }
 
     /**
      * Gets the set of digest algorithms with which the key can be used.
      */
-    public String[] getDigests() {
+    public @KeyStoreKeyProperties.DigestEnum String[] getDigests() {
         return ArrayUtils.cloneIfNotEmpty(mDigests);
     }
 
@@ -179,10 +179,10 @@
 
     /**
      * Gets the duration of time (seconds) for which this key can be used after the user is
-     * successfully authenticated.
+     * successfully authenticated. This has effect only if user authentication is required.
      *
-     * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication
-     *         is required for every use of the key.
+     * @return duration in seconds or {@code -1} if authentication is required for every use of the
+     *         key.
      *
      * @see #isUserAuthenticationRequired()
      */
diff --git a/keystore/java/android/security/KeyStoreParameter.java b/keystore/java/android/security/KeyStoreParameter.java
index b4747e9..8d7a19f 100644
--- a/keystore/java/android/security/KeyStoreParameter.java
+++ b/keystore/java/android/security/KeyStoreParameter.java
@@ -45,10 +45,10 @@
     private final Date mKeyValidityForOriginationEnd;
     private final Date mKeyValidityForConsumptionEnd;
     private final @KeyStoreKeyProperties.PurposeEnum int mPurposes;
-    private final String[] mEncryptionPaddings;
-    private final String[] mSignaturePaddings;
-    private final String[] mDigests;
-    private final String[] mBlockModes;
+    private final @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
+    private final @KeyStoreKeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
+    private final @KeyStoreKeyProperties.DigestEnum String[] mDigests;
+    private final @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
     private final boolean mRandomizedEncryptionRequired;
     private final boolean mUserAuthenticationRequired;
     private final int mUserAuthenticationValidityDurationSeconds;
@@ -60,10 +60,10 @@
             Date keyValidityForOriginationEnd,
             Date keyValidityForConsumptionEnd,
             @KeyStoreKeyProperties.PurposeEnum int purposes,
-            String[] encryptionPaddings,
-            String[] signaturePaddings,
-            String[] digests,
-            String[] blockModes,
+            @KeyStoreKeyProperties.EncryptionPaddingEnum String[] encryptionPaddings,
+            @KeyStoreKeyProperties.SignaturePaddingEnum String[] signaturePaddings,
+            @KeyStoreKeyProperties.DigestEnum String[] digests,
+            @KeyStoreKeyProperties.BlockModeEnum String[] blockModes,
             boolean randomizedEncryptionRequired,
             boolean userAuthenticationRequired,
             int userAuthenticationValidityDurationSeconds) {
@@ -151,7 +151,7 @@
     /**
      * Gets the set of padding schemes with which the key can be used when encrypting/decrypting.
      */
-    public String[] getEncryptionPaddings() {
+    public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() {
         return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings);
     }
 
@@ -159,7 +159,7 @@
      * Gets the set of padding schemes with which the key can be used when signing or verifying
      * signatures.
      */
-    public String[] getSignaturePaddings() {
+    public @KeyStoreKeyProperties.SignaturePaddingEnum String[] getSignaturePaddings() {
         return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings);
     }
 
@@ -170,7 +170,7 @@
      *
      * @see #isDigestsSpecified()
      */
-    public String[] getDigests() {
+    public @KeyStoreKeyProperties.DigestEnum String[] getDigests() {
         if (mDigests == null) {
             throw new IllegalStateException("Digests not specified");
         }
@@ -190,7 +190,7 @@
     /**
      * Gets the set of block modes with which the key can be used.
      */
-    public String[] getBlockModes() {
+    public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() {
         return ArrayUtils.cloneIfNotEmpty(mBlockModes);
     }
 
@@ -218,10 +218,12 @@
 
     /**
      * Gets the duration of time (seconds) for which this key can be used after the user is
-     * successfully authenticated.
+     * successfully authenticated. This has effect only if user authentication is required.
      *
-     * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication
-     *         is required for every use of the key.
+     * @return duration in seconds or {@code -1} if authentication is required for every use of the
+     *         key.
+     *
+     * @see #isUserAuthenticationRequired()
      */
     public int getUserAuthenticationValidityDurationSeconds() {
         return mUserAuthenticationValidityDurationSeconds;
@@ -251,10 +253,10 @@
         private Date mKeyValidityForOriginationEnd;
         private Date mKeyValidityForConsumptionEnd;
         private @KeyStoreKeyProperties.PurposeEnum int mPurposes;
-        private String[] mEncryptionPaddings;
-        private String[] mSignaturePaddings;
-        private String[] mDigests;
-        private String[] mBlockModes;
+        private @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
+        private @KeyStoreKeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
+        private @KeyStoreKeyProperties.DigestEnum String[] mDigests;
+        private @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
         private boolean mRandomizedEncryptionRequired = true;
         private boolean mUserAuthenticationRequired;
         private int mUserAuthenticationValidityDurationSeconds = -1;
@@ -292,6 +294,8 @@
          *
          * <p>By default, the key is valid at any instant.
          *
+         * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
+         *
          * @see #setKeyValidityEnd(Date)
          */
         public Builder setKeyValidityStart(Date startDate) {
@@ -304,6 +308,8 @@
          *
          * <p>By default, the key is valid at any instant.
          *
+         * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
+         *
          * @see #setKeyValidityStart(Date)
          * @see #setKeyValidityForConsumptionEnd(Date)
          * @see #setKeyValidityForOriginationEnd(Date)
@@ -319,6 +325,8 @@
          *
          * <p>By default, the key is valid at any instant.
          *
+         * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
+         *
          * @see #setKeyValidityForConsumptionEnd(Date)
          */
         public Builder setKeyValidityForOriginationEnd(Date endDate) {
@@ -332,6 +340,8 @@
          *
          * <p>By default, the key is valid at any instant.
          *
+         * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
+         *
          * @see #setKeyValidityForOriginationEnd(Date)
          */
         public Builder setKeyValidityForConsumptionEnd(Date endDate) {
@@ -343,6 +353,8 @@
          * Sets the set of purposes for which the key can be used.
          *
          * <p>This must be specified for all keys. There is no default.
+         *
+         * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
          */
         public Builder setPurposes(@KeyStoreKeyProperties.PurposeEnum int purposes) {
             mPurposes = purposes;
@@ -355,8 +367,11 @@
          * rejected.
          *
          * <p>This must be specified for keys which are used for encryption/decryption.
+         *
+         * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
          */
-        public Builder setEncryptionPaddings(String... paddings) {
+        public Builder setEncryptionPaddings(
+                @KeyStoreKeyProperties.EncryptionPaddingEnum String... paddings) {
             mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings);
             return this;
         }
@@ -367,8 +382,11 @@
          * rejected.
          *
          * <p>This must be specified for RSA keys which are used for signing/verification.
+         *
+         * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
          */
-        public Builder setSignaturePaddings(String... paddings) {
+        public Builder setSignaturePaddings(
+                @KeyStoreKeyProperties.SignaturePaddingEnum String... paddings) {
             mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(paddings);
             return this;
         }
@@ -380,8 +398,10 @@
          *
          * <p>For HMAC keys, the default is the digest specified in {@link Key#getAlgorithm()}. For
          * asymmetric signing keys this constraint must be specified.
+         *
+         * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
          */
-        public Builder setDigests(String... digests) {
+        public Builder setDigests(@KeyStoreKeyProperties.DigestEnum String... digests) {
             mDigests = ArrayUtils.cloneIfNotEmpty(digests);
             return this;
         }
@@ -391,8 +411,10 @@
          * Attempts to use the key with any other block modes will be rejected.
          *
          * <p>This must be specified for encryption/decryption keys.
+         *
+         * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
          */
-        public Builder setBlockModes(String... blockModes) {
+        public Builder setBlockModes(@KeyStoreKeyProperties.BlockModeEnum String... blockModes) {
             mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes);
             return this;
         }
@@ -430,6 +452,8 @@
          * <li>If you are using RSA encryption without padding, consider switching to padding
          * schemes which offer {@code IND-CPA}, such as PKCS#1 or OAEP.</li>
          * </ul>
+         *
+         * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
          */
         public Builder setRandomizedEncryptionRequired(boolean required) {
             mRandomizedEncryptionRequired = required;
@@ -449,6 +473,8 @@
          * <a href="{@docRoot}training/articles/keystore.html#UserAuthentication">More
          * information</a>.
          *
+         * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
+         *
          * @see #setUserAuthenticationValidityDurationSeconds(int)
          */
         public Builder setUserAuthenticationRequired(boolean required) {
@@ -462,7 +488,9 @@
          *
          * <p>By default, the user needs to authenticate for every use of the key.
          *
-         * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for
+         * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
+         *
+         * @param seconds duration in seconds or {@code -1} if the user needs to authenticate for
          *        every use of the key.
          *
          * @see #setUserAuthenticationRequired(boolean)
diff --git a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
index bfe09e3..ff79b7a 100644
--- a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
@@ -79,8 +79,8 @@
         int keySize;
         @KeyStoreKeyProperties.PurposeEnum int purposes;
         String[] encryptionPaddings;
-        String[] digests;
-        String[] blockModes;
+        @KeyStoreKeyProperties.DigestEnum String[] digests;
+        @KeyStoreKeyProperties.BlockModeEnum String[] blockModes;
         int keymasterSwEnforcedUserAuthenticators;
         int keymasterHwEnforcedUserAuthenticators;
         try {
@@ -105,10 +105,10 @@
 
             List<String> encryptionPaddingsList = new ArrayList<String>();
             for (int keymasterPadding : keyCharacteristics.getInts(KeymasterDefs.KM_TAG_PADDING)) {
-                String jcaPadding;
+                @KeyStoreKeyProperties.EncryptionPaddingEnum String jcaPadding;
                 try {
-                    jcaPadding = KeymasterUtils.getJcaEncryptionPaddingFromKeymasterPadding(
-                            keymasterPadding);
+                    jcaPadding =
+                            KeyStoreKeyProperties.EncryptionPadding.fromKeymaster(keymasterPadding);
                 } catch (IllegalArgumentException e) {
                     throw new InvalidKeySpecException(
                             "Unsupported encryption padding: " + keymasterPadding);
@@ -118,9 +118,9 @@
             encryptionPaddings =
                     encryptionPaddingsList.toArray(new String[encryptionPaddingsList.size()]);
 
-            digests = KeymasterUtils.getJcaDigestAlgorithmsFromKeymasterDigests(
+            digests = KeyStoreKeyProperties.Digest.allFromKeymaster(
                     keyCharacteristics.getInts(KeymasterDefs.KM_TAG_DIGEST));
-            blockModes = KeymasterUtils.getJcaBlockModesFromKeymasterBlockModes(
+            blockModes = KeyStoreKeyProperties.BlockMode.allFromKeymaster(
                     keyCharacteristics.getInts(KeymasterDefs.KM_TAG_BLOCK_MODE));
             keymasterSwEnforcedUserAuthenticators =
                     keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
diff --git a/keystore/java/android/security/KeymasterUtils.java b/keystore/java/android/security/KeymasterUtils.java
index aa44ecd..df67ae7 100644
--- a/keystore/java/android/security/KeymasterUtils.java
+++ b/keystore/java/android/security/KeymasterUtils.java
@@ -21,11 +21,6 @@
 import android.security.keymaster.KeymasterArguments;
 import android.security.keymaster.KeymasterDefs;
 
-import libcore.util.EmptyArray;
-
-import java.util.Collection;
-import java.util.Locale;
-
 /**
  * @hide
  */
@@ -33,152 +28,6 @@
 
     private KeymasterUtils() {}
 
-    public static int getKeymasterAlgorithmFromJcaSecretKeyAlgorithm(String jcaKeyAlgorithm) {
-        if ("AES".equalsIgnoreCase(jcaKeyAlgorithm)) {
-            return KeymasterDefs.KM_ALGORITHM_AES;
-        } else if (jcaKeyAlgorithm.toUpperCase(Locale.US).startsWith("HMAC")) {
-            return KeymasterDefs.KM_ALGORITHM_HMAC;
-        } else {
-            throw new IllegalArgumentException(
-                    "Unsupported secret key algorithm: " + jcaKeyAlgorithm);
-        }
-    }
-
-    public static String getJcaSecretKeyAlgorithm(int keymasterAlgorithm, int keymasterDigest) {
-        switch (keymasterAlgorithm) {
-            case KeymasterDefs.KM_ALGORITHM_AES:
-                if (keymasterDigest != -1) {
-                    throw new IllegalArgumentException(
-                            "Digest not supported for AES key: " + keymasterDigest);
-                }
-                return "AES";
-            case KeymasterDefs.KM_ALGORITHM_HMAC:
-                switch (keymasterDigest) {
-                    case KeymasterDefs.KM_DIGEST_SHA1:
-                        return "HmacSHA1";
-                    case KeymasterDefs.KM_DIGEST_SHA_2_224:
-                        return "HmacSHA224";
-                    case KeymasterDefs.KM_DIGEST_SHA_2_256:
-                        return "HmacSHA256";
-                    case KeymasterDefs.KM_DIGEST_SHA_2_384:
-                        return "HmacSHA384";
-                    case KeymasterDefs.KM_DIGEST_SHA_2_512:
-                        return "HmacSHA512";
-                    default:
-                        throw new IllegalArgumentException(
-                                "Unsupported HMAC digest: " + keymasterDigest);
-                }
-            default:
-                throw new IllegalArgumentException("Unsupported algorithm: " + keymasterAlgorithm);
-        }
-    }
-
-    public static String getJcaKeyPairAlgorithmFromKeymasterAlgorithm(int keymasterAlgorithm) {
-        switch (keymasterAlgorithm) {
-            case KeymasterDefs.KM_ALGORITHM_RSA:
-                return "RSA";
-            case KeymasterDefs.KM_ALGORITHM_EC:
-                return "EC";
-            default:
-                throw new IllegalArgumentException("Unsupported algorithm: " + keymasterAlgorithm);
-        }
-    }
-
-    public static int getKeymasterDigestfromJcaSecretKeyAlgorithm(String jcaKeyAlgorithm) {
-        String algorithmUpper = jcaKeyAlgorithm.toUpperCase(Locale.US);
-        if (algorithmUpper.startsWith("HMAC")) {
-            String digestUpper = algorithmUpper.substring("HMAC".length());
-            switch (digestUpper) {
-                case "MD5":
-                    return KeymasterDefs.KM_DIGEST_MD5;
-                case "SHA1":
-                    return KeymasterDefs.KM_DIGEST_SHA1;
-                case "SHA224":
-                    return KeymasterDefs.KM_DIGEST_SHA_2_224;
-                case "SHA256":
-                    return KeymasterDefs.KM_DIGEST_SHA_2_256;
-                case "SHA384":
-                    return KeymasterDefs.KM_DIGEST_SHA_2_384;
-                case "SHA512":
-                    return KeymasterDefs.KM_DIGEST_SHA_2_512;
-                default:
-                    throw new IllegalArgumentException("Unsupported HMAC digest: " + digestUpper);
-            }
-        } else {
-            return -1;
-        }
-    }
-
-    public static int getKeymasterDigestFromJcaDigestAlgorithm(String jcaDigestAlgorithm) {
-        if (jcaDigestAlgorithm.equalsIgnoreCase("SHA-1")) {
-            return KeymasterDefs.KM_DIGEST_SHA1;
-        } else if (jcaDigestAlgorithm.equalsIgnoreCase("SHA-224")) {
-            return KeymasterDefs.KM_DIGEST_SHA_2_224;
-        } else if (jcaDigestAlgorithm.equalsIgnoreCase("SHA-256")) {
-            return KeymasterDefs.KM_DIGEST_SHA_2_256;
-        } else if (jcaDigestAlgorithm.equalsIgnoreCase("SHA-384")) {
-            return KeymasterDefs.KM_DIGEST_SHA_2_384;
-        } else if (jcaDigestAlgorithm.equalsIgnoreCase("SHA-512")) {
-            return KeymasterDefs.KM_DIGEST_SHA_2_512;
-        } else if (jcaDigestAlgorithm.equalsIgnoreCase("NONE")) {
-            return KeymasterDefs.KM_DIGEST_NONE;
-        } else if (jcaDigestAlgorithm.equalsIgnoreCase("MD5")) {
-            return KeymasterDefs.KM_DIGEST_MD5;
-        } else {
-            throw new IllegalArgumentException(
-                    "Unsupported digest algorithm: " + jcaDigestAlgorithm);
-        }
-    }
-
-    public static String getJcaDigestAlgorithmFromKeymasterDigest(int keymasterDigest) {
-        switch (keymasterDigest) {
-            case KeymasterDefs.KM_DIGEST_NONE:
-                return "NONE";
-            case KeymasterDefs.KM_DIGEST_MD5:
-                return "MD5";
-            case KeymasterDefs.KM_DIGEST_SHA1:
-                return "SHA-1";
-            case KeymasterDefs.KM_DIGEST_SHA_2_224:
-                return "SHA-224";
-            case KeymasterDefs.KM_DIGEST_SHA_2_256:
-                return "SHA-256";
-            case KeymasterDefs.KM_DIGEST_SHA_2_384:
-                return "SHA-384";
-            case KeymasterDefs.KM_DIGEST_SHA_2_512:
-                return "SHA-512";
-            default:
-                throw new IllegalArgumentException(
-                        "Unsupported digest algorithm: " + keymasterDigest);
-        }
-    }
-
-    public static String[] getJcaDigestAlgorithmsFromKeymasterDigests(
-            Collection<Integer> keymasterDigests) {
-        if (keymasterDigests.isEmpty()) {
-            return EmptyArray.STRING;
-        }
-        String[] result = new String[keymasterDigests.size()];
-        int offset = 0;
-        for (int keymasterDigest : keymasterDigests) {
-            result[offset] = getJcaDigestAlgorithmFromKeymasterDigest(keymasterDigest);
-            offset++;
-        }
-        return result;
-    }
-
-    public static int[] getKeymasterDigestsFromJcaDigestAlgorithms(String[] jcaDigestAlgorithms) {
-        if ((jcaDigestAlgorithms == null) || (jcaDigestAlgorithms.length == 0)) {
-            return EmptyArray.INT;
-        }
-        int[] result = new int[jcaDigestAlgorithms.length];
-        int offset = 0;
-        for (String jcaDigestAlgorithm : jcaDigestAlgorithms) {
-            result[offset] = getKeymasterDigestFromJcaDigestAlgorithm(jcaDigestAlgorithm);
-            offset++;
-        }
-        return result;
-    }
-
     public static int getDigestOutputSizeBits(int keymasterDigest) {
         switch (keymasterDigest) {
             case KeymasterDefs.KM_DIGEST_NONE:
@@ -200,60 +49,6 @@
         }
     }
 
-    public static int getKeymasterBlockModeFromJcaBlockMode(String jcaBlockMode) {
-        if ("ECB".equalsIgnoreCase(jcaBlockMode)) {
-            return KeymasterDefs.KM_MODE_ECB;
-        } else if ("CBC".equalsIgnoreCase(jcaBlockMode)) {
-            return KeymasterDefs.KM_MODE_CBC;
-        } else if ("CTR".equalsIgnoreCase(jcaBlockMode)) {
-            return KeymasterDefs.KM_MODE_CTR;
-        } else if ("GCM".equalsIgnoreCase(jcaBlockMode)) {
-            return KeymasterDefs.KM_MODE_GCM;
-        } else {
-            throw new IllegalArgumentException("Unsupported block mode: " + jcaBlockMode);
-        }
-    }
-
-    public static String getJcaBlockModeFromKeymasterBlockMode(int keymasterBlockMode) {
-        switch (keymasterBlockMode) {
-            case KeymasterDefs.KM_MODE_ECB:
-                return "ECB";
-            case KeymasterDefs.KM_MODE_CBC:
-                return "CBC";
-            case KeymasterDefs.KM_MODE_CTR:
-                return "CTR";
-            case KeymasterDefs.KM_MODE_GCM:
-                return "GCM";
-            default:
-                throw new IllegalArgumentException("Unsupported block mode: " + keymasterBlockMode);
-        }
-    }
-
-    public static String[] getJcaBlockModesFromKeymasterBlockModes(
-            Collection<Integer> keymasterBlockModes) {
-        if ((keymasterBlockModes == null) || (keymasterBlockModes.isEmpty())) {
-            return EmptyArray.STRING;
-        }
-        String[] result = new String[keymasterBlockModes.size()];
-        int offset = 0;
-        for (int keymasterBlockMode : keymasterBlockModes) {
-            result[offset] = getJcaBlockModeFromKeymasterBlockMode(keymasterBlockMode);
-            offset++;
-        }
-        return result;
-    }
-
-    public static int[] getKeymasterBlockModesFromJcaBlockModes(String[] jcaBlockModes) {
-        if ((jcaBlockModes == null) || (jcaBlockModes.length == 0)) {
-            return EmptyArray.INT;
-        }
-        int[] result = new int[jcaBlockModes.length];
-        for (int i = 0; i < jcaBlockModes.length; i++) {
-            result[i] = getKeymasterBlockModeFromJcaBlockMode(jcaBlockModes[i]);
-        }
-        return result;
-    }
-
     public static boolean isKeymasterBlockModeIndCpaCompatible(int keymasterBlockMode) {
         switch (keymasterBlockMode) {
             case KeymasterDefs.KM_MODE_ECB:
@@ -267,82 +62,6 @@
         }
     }
 
-    public static int getKeymasterPaddingFromJcaEncryptionPadding(String jcaPadding) {
-        if ("NoPadding".equalsIgnoreCase(jcaPadding)) {
-            return KeymasterDefs.KM_PAD_NONE;
-        } else if ("PKCS7Padding".equalsIgnoreCase(jcaPadding)) {
-            return KeymasterDefs.KM_PAD_PKCS7;
-        } else if ("PKCS1Padding".equalsIgnoreCase(jcaPadding)) {
-            return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT;
-        } else if ("OEAPPadding".equalsIgnoreCase(jcaPadding)) {
-            return KeymasterDefs.KM_PAD_RSA_OAEP;
-        } else {
-            throw new IllegalArgumentException(
-                    "Unsupported encryption padding scheme: " + jcaPadding);
-        }
-    }
-
-    public static String getJcaEncryptionPaddingFromKeymasterPadding(int keymasterPadding) {
-        switch (keymasterPadding) {
-            case KeymasterDefs.KM_PAD_NONE:
-                return "NoPadding";
-            case KeymasterDefs.KM_PAD_PKCS7:
-                return "PKCS7Padding";
-            case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT:
-                return "PKCS1Padding";
-            case KeymasterDefs.KM_PAD_RSA_OAEP:
-                return "OEAPPadding";
-            default:
-                throw new IllegalArgumentException(
-                        "Unsupported encryption padding: " + keymasterPadding);
-        }
-    }
-
-    public static int getKeymasterPaddingFromJcaSignaturePadding(String jcaPadding) {
-        if ("PKCS#1".equalsIgnoreCase(jcaPadding)) {
-            return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN;
-        } if ("PSS".equalsIgnoreCase(jcaPadding)) {
-            return KeymasterDefs.KM_PAD_RSA_PSS;
-        } else {
-            throw new IllegalArgumentException(
-                    "Unsupported signature padding scheme: " + jcaPadding);
-        }
-    }
-
-    public static String getJcaSignaturePaddingFromKeymasterPadding(int keymasterPadding) {
-        switch (keymasterPadding) {
-            case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN:
-                return "PKCS#1";
-            case KeymasterDefs.KM_PAD_RSA_PSS:
-                return "PSS";
-            default:
-                throw new IllegalArgumentException(
-                        "Unsupported signature padding: " + keymasterPadding);
-        }
-    }
-
-    public static int[] getKeymasterPaddingsFromJcaEncryptionPaddings(String[] jcaPaddings) {
-        if ((jcaPaddings == null) || (jcaPaddings.length == 0)) {
-            return EmptyArray.INT;
-        }
-        int[] result = new int[jcaPaddings.length];
-        for (int i = 0; i < jcaPaddings.length; i++) {
-            result[i] = getKeymasterPaddingFromJcaEncryptionPadding(jcaPaddings[i]);
-        }
-        return result;
-    }
-
-    public static int[] getKeymasterPaddingsFromJcaSignaturePaddings(String[] jcaPaddings) {
-        if ((jcaPaddings == null) || (jcaPaddings.length == 0)) {
-            return EmptyArray.INT;
-        }
-        int[] result = new int[jcaPaddings.length];
-        for (int i = 0; i < jcaPaddings.length; i++) {
-            result[i] = getKeymasterPaddingFromJcaSignaturePadding(jcaPaddings[i]);
-        }
-        return result;
-    }
-
     /**
      * Adds keymaster arguments to express the key's authorization policy supported by user
      * authentication.
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 19a5beb..2ae7b08 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -3336,6 +3336,30 @@
     return NO_ERROR;
 }
 
+status_t ResTable::Theme::clear()
+{
+    if (kDebugTableTheme) {
+        ALOGI("Clearing theme %p...\n", this);
+        dumpToLog();
+    }
+
+    for (size_t i = 0; i < Res_MAXPACKAGE; i++) {
+        if (mPackages[i] != NULL) {
+            free_package(mPackages[i]);
+            mPackages[i] = NULL;
+        }
+    }
+
+    mTypeSpecFlags = 0;
+
+    if (kDebugTableTheme) {
+        ALOGI("Final theme:");
+        dumpToLog();
+    }
+
+    return NO_ERROR;
+}
+
 ssize_t ResTable::Theme::getAttribute(uint32_t resID, Res_value* outValue,
         uint32_t* outTypeSpecFlags) const
 {
diff --git a/libs/hwui/AssetAtlas.cpp b/libs/hwui/AssetAtlas.cpp
index 882826e..2889d2f 100644
--- a/libs/hwui/AssetAtlas.cpp
+++ b/libs/hwui/AssetAtlas.cpp
@@ -112,9 +112,6 @@
     Texture* const mDelegate;
 }; // struct DelegateTexture
 
-/**
- * TODO: This method does not take the rotation flag into account
- */
 void AssetAtlas::createEntries(Caches& caches, int64_t* map, int count) {
     const float width = float(mTexture->width);
     const float height = float(mTexture->height);
@@ -128,7 +125,6 @@
         // pointers on 64 bit architectures.
         const int x = static_cast<int>(map[i++]);
         const int y = static_cast<int>(map[i++]);
-        bool rotated = map[i++] > 0;
 
         // Bitmaps should never be null, we're just extra paranoid
         if (!pixelRef) continue;
@@ -142,7 +138,7 @@
         texture->width = pixelRef->info().width();
         texture->height = pixelRef->info().height();
 
-        Entry* entry = new Entry(pixelRef, x, y, rotated, texture, mapper, *this);
+        Entry* entry = new Entry(pixelRef, texture, mapper, *this);
         texture->uvMapper = &entry->uvMapper;
 
         mEntries.add(entry->pixelRef, entry);
diff --git a/libs/hwui/AssetAtlas.h b/libs/hwui/AssetAtlas.h
index 17c5281..f1cd0b4 100644
--- a/libs/hwui/AssetAtlas.h
+++ b/libs/hwui/AssetAtlas.h
@@ -45,8 +45,8 @@
 class AssetAtlas {
 public:
     /**
-     * Entry representing the position and rotation of a
-     * bitmap inside the atlas.
+     * Entry representing the texture and uvMapper of a PixelRef in the
+     * atlas
      */
     class Entry {
     public:
@@ -78,30 +78,15 @@
         SkPixelRef* pixelRef;
 
         /**
-         * Location of the bitmap inside the atlas, in pixels.
-         */
-        int x;
-        int y;
-
-        /**
-         * If set, the bitmap is rotated 90 degrees (clockwise)
-         * inside the atlas.
-         */
-        bool rotated;
-
-        /**
          * Atlas this entry belongs to.
          */
         const AssetAtlas& atlas;
 
-        Entry(SkPixelRef* pixelRef, int x, int y, bool rotated,
-                    Texture* texture, const UvMapper& mapper, const AssetAtlas& atlas)
+        Entry(SkPixelRef* pixelRef, Texture* texture, const UvMapper& mapper,
+                    const AssetAtlas& atlas)
                 : texture(texture)
                 , uvMapper(mapper)
                 , pixelRef(pixelRef)
-                , x(x)
-                , y(y)
-                , rotated(rotated)
                 , atlas(atlas) {
         }
 
@@ -120,8 +105,7 @@
      * Initializes the atlas with the specified buffer and
      * map. The buffer is a gralloc'd texture that will be
      * used as an EGLImage. The map is a list of SkBitmap*
-     * and their (x, y) positions as well as their rotation
-     * flags.
+     * and their (x, y) positions
      *
      * This method returns immediately if the atlas is already
      * initialized. To re-initialize the atlas, you must
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 4540bec..e679bff 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -41,10 +41,6 @@
     ResourceCache& resourceCache = ResourceCache::getInstance();
     resourceCache.lock();
 
-    for (size_t i = 0; i < bitmapResources.size(); i++) {
-        resourceCache.decrementRefcountLocked(bitmapResources.itemAt(i));
-    }
-
     for (size_t i = 0; i < patchResources.size(); i++) {
         resourceCache.decrementRefcountLocked(patchResources.itemAt(i));
     }
@@ -59,7 +55,6 @@
         delete path;
     }
 
-    bitmapResources.clear();
     patchResources.clear();
     pathResources.clear();
     paints.clear();
diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h
index 0064236..d997ef4 100644
--- a/libs/hwui/DisplayListCanvas.h
+++ b/libs/hwui/DisplayListCanvas.h
@@ -350,9 +350,10 @@
         // correctly, such as creating the bitmap from scratch, drawing with it, changing its
         // contents, and drawing again. The only fix would be to always copy it the first time,
         // which doesn't seem worth the extra cycles for this unlikely case.
-        const SkBitmap* cachedBitmap = mResourceCache.insert(bitmap);
-        mDisplayListData->bitmapResources.add(cachedBitmap);
-        return cachedBitmap;
+        SkBitmap* localBitmap = new (alloc()) SkBitmap(bitmap);
+        alloc().autoDestroy(localBitmap);
+        mDisplayListData->bitmapResources.push_back(localBitmap);
+        return localBitmap;
     }
 
     inline const Res_png_9patch* refPatch(const Res_png_9patch* patch) {
diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp
index 454fedc..75d8134 100644
--- a/libs/hwui/ResourceCache.cpp
+++ b/libs/hwui/ResourceCache.cpp
@@ -59,21 +59,6 @@
     mLock.unlock();
 }
 
-const SkBitmap* ResourceCache::insert(const SkBitmap& bitmapResource) {
-    Mutex::Autolock _l(mLock);
-
-    BitmapKey bitmapKey(bitmapResource);
-    ssize_t index = mBitmapCache.indexOfKey(bitmapKey);
-    if (index == NAME_NOT_FOUND) {
-        SkBitmap* cachedBitmap = new SkBitmap(bitmapResource);
-        index = mBitmapCache.add(bitmapKey, cachedBitmap);
-        return cachedBitmap;
-    }
-
-    mBitmapCache.keyAt(index).mRefCount++;
-    return mBitmapCache.valueAt(index);
-}
-
 void ResourceCache::incrementRefcount(void* resource, ResourceType resourceType) {
     Mutex::Autolock _l(mLock);
     incrementRefcountLocked(resource, resourceType);
@@ -98,11 +83,6 @@
     decrementRefcountLocked(resource);
 }
 
-void ResourceCache::decrementRefcount(const SkBitmap* bitmapResource) {
-    Mutex::Autolock _l(mLock);
-    decrementRefcountLocked(bitmapResource);
-}
-
 void ResourceCache::decrementRefcount(const Res_png_9patch* patchResource) {
     decrementRefcount((void*) patchResource);
 }
@@ -120,23 +100,6 @@
     }
 }
 
-void ResourceCache::decrementRefcountLocked(const SkBitmap* bitmapResource) {
-    BitmapKey bitmapKey(*bitmapResource);
-    ssize_t index = mBitmapCache.indexOfKey(bitmapKey);
-
-    LOG_ALWAYS_FATAL_IF(index == NAME_NOT_FOUND,
-                    "Decrementing the reference of an untracked Bitmap");
-
-    const BitmapKey& cacheEntry = mBitmapCache.keyAt(index);
-    if (cacheEntry.mRefCount == 1) {
-        // delete the bitmap and remove it from the cache
-        delete mBitmapCache.valueAt(index);
-        mBitmapCache.removeItemsAt(index);
-    } else {
-        cacheEntry.mRefCount--;
-    }
-}
-
 void ResourceCache::decrementRefcountLocked(const Res_png_9patch* patchResource) {
     decrementRefcountLocked((void*) patchResource);
 }
@@ -190,38 +153,5 @@
     delete ref;
 }
 
-///////////////////////////////////////////////////////////////////////////////
-// Bitmap Key
-///////////////////////////////////////////////////////////////////////////////
-
-void BitmapKey::operator=(const BitmapKey& other) {
-    this->mRefCount = other.mRefCount;
-    this->mBitmapDimensions = other.mBitmapDimensions;
-    this->mPixelRefOrigin = other.mPixelRefOrigin;
-    this->mPixelRefStableID = other.mPixelRefStableID;
-}
-
-bool BitmapKey::operator==(const BitmapKey& other) const {
-    return mPixelRefStableID == other.mPixelRefStableID &&
-           mPixelRefOrigin == other.mPixelRefOrigin &&
-           mBitmapDimensions == other.mBitmapDimensions;
-}
-
-bool BitmapKey::operator<(const BitmapKey& other) const {
-    if (mPixelRefStableID != other.mPixelRefStableID) {
-        return mPixelRefStableID < other.mPixelRefStableID;
-    }
-    if (mPixelRefOrigin.x() != other.mPixelRefOrigin.x()) {
-        return mPixelRefOrigin.x() < other.mPixelRefOrigin.x();
-    }
-    if (mPixelRefOrigin.y() != other.mPixelRefOrigin.y()) {
-        return mPixelRefOrigin.y() < other.mPixelRefOrigin.y();
-    }
-    if (mBitmapDimensions.width() != other.mBitmapDimensions.width()) {
-        return mBitmapDimensions.width() < other.mBitmapDimensions.width();
-    }
-    return mBitmapDimensions.height() < other.mBitmapDimensions.height();
-}
-
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h
index 6c483fa..4583c8d 100644
--- a/libs/hwui/ResourceCache.h
+++ b/libs/hwui/ResourceCache.h
@@ -51,37 +51,6 @@
     ResourceType resourceType;
 };
 
-class BitmapKey {
-public:
-    BitmapKey(const SkBitmap& bitmap)
-        : mRefCount(1)
-        , mBitmapDimensions(bitmap.dimensions())
-        , mPixelRefOrigin(bitmap.pixelRefOrigin())
-        , mPixelRefStableID(bitmap.pixelRef()->getStableID()) { }
-
-    void operator=(const BitmapKey& other);
-    bool operator==(const BitmapKey& other) const;
-    bool operator<(const BitmapKey& other) const;
-
-private:
-    // This constructor is only used by the KeyedVector implementation
-    BitmapKey()
-        : mRefCount(-1)
-        , mBitmapDimensions(SkISize::Make(0,0))
-        , mPixelRefOrigin(SkIPoint::Make(0,0))
-        , mPixelRefStableID(0) { }
-
-    // reference count of all HWUI object using this bitmap
-    mutable int mRefCount;
-
-    SkISize mBitmapDimensions;
-    SkIPoint mPixelRefOrigin;
-    uint32_t mPixelRefStableID;
-
-    friend class ResourceCache;
-    friend struct android::key_value_pair_t<BitmapKey, SkBitmap*>;
-};
-
 class ANDROID_API ResourceCache: public Singleton<ResourceCache> {
     ResourceCache();
     ~ResourceCache();
@@ -97,18 +66,10 @@
     void lock();
     void unlock();
 
-    /**
-     * The cache stores a copy of the provided resource or refs an existing resource
-     * if the bitmap has previously been inserted and returns the cached copy.
-     */
-    const SkBitmap* insert(const SkBitmap& resource);
-
     void incrementRefcount(const Res_png_9patch* resource);
 
-    void decrementRefcount(const SkBitmap* resource);
     void decrementRefcount(const Res_png_9patch* resource);
 
-    void decrementRefcountLocked(const SkBitmap* resource);
     void decrementRefcountLocked(const Res_png_9patch* resource);
 
     void destructor(Res_png_9patch* resource);
@@ -134,7 +95,6 @@
     mutable Mutex mLock;
 
     KeyedVector<const void*, ResourceReference*>* mCache;
-    KeyedVector<BitmapKey, SkBitmap*> mBitmapCache;
 };
 
 }; // namespace uirenderer
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index c643e1d..17e47b9 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -353,7 +353,6 @@
 }
 
 void RenderProxy::overrideProperty(const char* name, const char* value) {
-    RenderThread& thread = RenderThread::getInstance();
     SETUP_TASK(overrideProperty);
     args->name = name;
     args->value = value;
diff --git a/media/jni/android_media_MediaSync.h b/media/jni/android_media_MediaSync.h
index fa5e5e0..5823057 100644
--- a/media/jni/android_media_MediaSync.h
+++ b/media/jni/android_media_MediaSync.h
@@ -26,7 +26,7 @@
 
 struct AudioPlaybackRate;
 class AudioTrack;
-struct IGraphicBufferProducer;
+class IGraphicBufferProducer;
 struct MediaClock;
 class MediaSync;
 
diff --git a/native/android/sensor.cpp b/native/android/sensor.cpp
index 4e7c6be..26b41e8 100644
--- a/native/android/sensor.cpp
+++ b/native/android/sensor.cpp
@@ -35,9 +35,27 @@
 using android::SensorManager;
 using android::SensorEventQueue;
 using android::String8;
+using android::String16;
 
 /*****************************************************************************/
 
+android::Mutex android::SensorManager::sLock;
+std::map<String16, SensorManager*> android::SensorManager::sPackageInstances;
+
+ASensorManager* ASensorManager_getInstance()
+{
+    return ASensorManager_getInstanceForPackage(NULL);
+}
+
+ASensorManager* ASensorManager_getInstanceForPackage(const char* packageName)
+{
+    if (packageName) {
+        return &SensorManager::getInstanceForPackage(String16(packageName));
+    } else {
+        return &SensorManager::getInstanceForPackage(String16());
+    }
+}
+
 int ASensorManager_getSensorList(ASensorManager* manager,
         ASensorList* list)
 {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index b5c1ca8..f352849 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -33,6 +33,7 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.FontSizeUtils;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile.DetailAdapter;
@@ -182,8 +183,11 @@
     public void setExpanded(boolean expanded) {
         if (mExpanded == expanded) return;
         mExpanded = expanded;
+        MetricsLogger.visibility(mContext, MetricsLogger.QS_PANEL, mExpanded);
         if (!mExpanded) {
             closeDetail();
+        } else {
+            logTiles();
         }
     }
 
@@ -365,9 +369,11 @@
             mDetailContent.removeAllViews();
             mDetail.bringToFront();
             mDetailContent.addView(r.detailView);
+            MetricsLogger.visible(mContext, detailAdapter.getMetricsCategory());
             setDetailRecord(r);
             listener = mHideGridContentWhenDone;
         } else {
+            MetricsLogger.hidden(mContext, mDetailRecord.detailAdapter.getMetricsCategory());
             mClosingDetail = true;
             setGridContentVisibility(true);
             listener = mTeardownDetailWhenDone;
@@ -387,9 +393,21 @@
             }
         }
         mBrightnessView.setVisibility(newVis);
+        if (mGridContentVisible != visible) {
+            MetricsLogger.visibility(mContext, MetricsLogger.QS_PANEL, newVis);
+        }
         mGridContentVisible = visible;
     }
 
+    private void logTiles() {
+        for (int i = 0; i < mRecords.size(); i++) {
+            TileRecord tileRecord = mRecords.get(i);
+            if (tileRecord.tile.getState().visible) {
+                MetricsLogger.visible(mContext, tileRecord.tile.getMetricsCategory());
+            }
+        }
+    }
+
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         final int width = MeasureSpec.getSize(widthMeasureSpec);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index b9574dc..452fd44 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -29,6 +29,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.qs.QSTile.State;
 import com.android.systemui.statusbar.policy.BluetoothController;
 import com.android.systemui.statusbar.policy.CastController;
@@ -66,9 +67,17 @@
     private boolean mAnnounceNextStateChange;
 
     abstract protected TState newTileState();
-    abstract protected void handleClick();
     abstract protected void handleUpdateState(TState state, Object arg);
 
+    /**
+     * Declare the category of this tile.
+     *
+     * Categories are defined in {@link com.android.internal.logging.MetricsLogger}
+     * or if there is no relevant existing category you may define one in
+     * {@link com.android.systemui.qs.QSTile}.
+     */
+    abstract public int getMetricsCategory();
+
     protected QSTile(Host host) {
         mHost = host;
         mContext = host.getContext();
@@ -97,6 +106,7 @@
         View createDetailView(Context context, View convertView, ViewGroup parent);
         Intent getSettingsIntent();
         void setToggleState(boolean state);
+        int getMetricsCategory();
     }
 
     // safe to call from any thread
@@ -160,6 +170,10 @@
         handleRefreshState(null);
     }
 
+    protected void handleClick() {
+        MetricsLogger.action(mContext, getMetricsCategory(), getMetricsPackage());
+    };
+
     protected void handleSecondaryClick() {
         // optional
     }
@@ -168,6 +182,10 @@
         // optional
     }
 
+    protected String getMetricsPackage() {
+        return "";
+    }
+
     protected void handleRefreshState(Object arg) {
         handleUpdateState(mTmpState, arg);
         final boolean changed = mTmpState.copyTo(mState);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index 2bc31fc..6744154 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -23,6 +23,7 @@
 import android.net.ConnectivityManager;
 import android.provider.Settings.Global;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.qs.GlobalSetting;
 import com.android.systemui.qs.QSTile;
@@ -55,6 +56,7 @@
 
     @Override
     public void handleClick() {
+        super.handleClick();
         setEnabled(!mState.value);
         mEnable.setAllowAnimation(true);
         mDisable.setAllowAnimation(true);
@@ -85,6 +87,11 @@
     }
 
     @Override
+    public int getMetricsCategory() {
+        return MetricsLogger.QS_AIRPLANEMODE;
+    }
+
+    @Override
     protected String composeChangeAnnouncement() {
         if (mState.value) {
             return mContext.getString(R.string.accessibility_quick_settings_airplane_changed_on);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index b42b5f6..8eb624f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -25,6 +25,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSDetailItems;
@@ -74,6 +75,7 @@
 
     @Override
     protected void handleClick() {
+        super.handleClick();
         final boolean isEnabled = (Boolean)mState.value;
         mController.setBluetoothEnabled(!isEnabled);
     }
@@ -132,6 +134,11 @@
     }
 
     @Override
+    public int getMetricsCategory() {
+        return MetricsLogger.QS_BLUETOOTH;
+    }
+
+    @Override
     protected String composeChangeAnnouncement() {
         if (mState.value) {
             return mContext.getString(R.string.accessibility_quick_settings_bluetooth_changed_on);
@@ -182,6 +189,11 @@
         }
 
         @Override
+        public int getMetricsCategory() {
+            return MetricsLogger.QS_BLUETOOTH_DETAILS;
+        }
+
+        @Override
         public View createDetailView(Context context, View convertView, ViewGroup parent) {
             mItems = QSDetailItems.convertOrInflate(context, convertView, parent);
             mItems.setTagSuffix("Bluetooth");
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 5bf6fb5..a3d7bcc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -24,6 +24,7 @@
 import android.view.View.OnAttachStateChangeListener;
 import android.view.ViewGroup;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSDetailItems;
 import com.android.systemui.qs.QSDetailItems.Item;
@@ -85,6 +86,7 @@
 
     @Override
     protected void handleClick() {
+        super.handleClick();
         showDetail(true);
     }
 
@@ -113,6 +115,11 @@
     }
 
     @Override
+    public int getMetricsCategory() {
+        return MetricsLogger.QS_CAST;
+    }
+
+    @Override
     protected String composeChangeAnnouncement() {
         if (!mState.value) {
             // We only announce when it's turned off to avoid vocal overflow.
@@ -164,6 +171,11 @@
         }
 
         @Override
+        public int getMetricsCategory() {
+            return MetricsLogger.QS_CAST_DETAILS;
+        }
+
+        @Override
         public View createDetailView(Context context, View convertView, ViewGroup parent) {
             mItems = QSDetailItems.convertOrInflate(context, convertView, parent);
             mItems.setTagSuffix("Cast");
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 30f92b9..0026141 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -24,6 +24,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.qs.QSTileView;
@@ -75,6 +76,7 @@
 
     @Override
     protected void handleClick() {
+        super.handleClick();
         if (mDataController.isMobileDataSupported()) {
             showDetail(true);
         } else {
@@ -118,6 +120,11 @@
                 state.label);
     }
 
+    @Override
+    public int getMetricsCategory() {
+        return MetricsLogger.QS_CELLULAR;
+    }
+
     // Remove the period from the network name
     public static String removeTrailingPeriod(String string) {
         if (string == null) return null;
@@ -227,6 +234,11 @@
         }
 
         @Override
+        public int getMetricsCategory() {
+            return MetricsLogger.QS_DATAUSAGEDETAIL;
+        }
+
+        @Override
         public View createDetailView(Context context, View convertView, ViewGroup parent) {
             final DataUsageDetailView v = (DataUsageDetailView) (convertView != null
                     ? convertView
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index 4a33f55..6fa094e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -18,6 +18,7 @@
 
 import android.provider.Settings.Secure;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
@@ -86,6 +87,7 @@
 
     @Override
     protected void handleClick() {
+        super.handleClick();
         mSetting.setValue(mState.value ? 0 : 1);
         mEnable.setAllowAnimation(true);
         mDisable.setAllowAnimation(true);
@@ -115,6 +117,11 @@
     }
 
     @Override
+    public int getMetricsCategory() {
+        return MetricsLogger.QS_COLORINVERSION;
+    }
+
+    @Override
     protected String composeChangeAnnouncement() {
         if (mState.value) {
             return mContext.getString(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 5145bc7..e708a72 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -29,6 +29,7 @@
 import android.view.View.OnAttachStateChangeListener;
 import android.view.ViewGroup;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
@@ -88,6 +89,7 @@
 
     @Override
     public void handleClick() {
+        super.handleClick();
         if (mState.value) {
             mController.setZen(Global.ZEN_MODE_OFF, null, TAG);
         } else {
@@ -135,6 +137,11 @@
     }
 
     @Override
+    public int getMetricsCategory() {
+        return MetricsLogger.QS_DND;
+    }
+
+    @Override
     protected String composeChangeAnnouncement() {
         if (mState.value) {
             return mContext.getString(R.string.accessibility_quick_settings_dnd_changed_on);
@@ -209,6 +216,11 @@
         }
 
         @Override
+        public int getMetricsCategory() {
+            return MetricsLogger.QS_DND_DETAILS;
+        }
+
+        @Override
         public View createDetailView(Context context, View convertView, ViewGroup parent) {
             final ZenModePanel zmp = convertView != null ? (ZenModePanel) convertView
                     : (ZenModePanel) LayoutInflater.from(context).inflate(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index cb78deb..a1f3cde 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -18,6 +18,7 @@
 
 import android.app.ActivityManager;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.statusbar.policy.FlashlightController;
@@ -59,6 +60,7 @@
 
     @Override
     protected void handleClick() {
+        super.handleClick();
         if (ActivityManager.isUserAMonkey()) {
             return;
         }
@@ -84,6 +86,11 @@
     }
 
     @Override
+    public int getMetricsCategory() {
+        return MetricsLogger.QS_FLASHLIGHT;
+    }
+
+    @Override
     protected String composeChangeAnnouncement() {
         if (mState.value) {
             return mContext.getString(R.string.accessibility_quick_settings_flashlight_changed_on);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 6063f80..b864ff4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.Intent;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.qs.UsageTracker;
@@ -68,6 +69,7 @@
 
     @Override
     protected void handleClick() {
+        super.handleClick();
         final boolean isEnabled = (Boolean) mState.value;
         mController.setHotspotEnabled(!isEnabled);
         mEnable.setAllowAnimation(true);
@@ -97,6 +99,11 @@
     }
 
     @Override
+    public int getMetricsCategory() {
+        return MetricsLogger.QS_HOTSPOT;
+    }
+
+    @Override
     protected String composeChangeAnnouncement() {
         if (mState.value) {
             return mContext.getString(R.string.accessibility_quick_settings_hotspot_changed_on);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
index 2736530..20b5f04 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
@@ -29,6 +29,7 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.qs.QSTile;
 
 import java.util.Arrays;
@@ -42,6 +43,7 @@
     private PendingIntent mOnLongClick;
     private String mOnLongClickUri;
     private int mCurrentUserId;
+    private String mIntentPackage;
 
     private IntentTile(Host host, String action) {
         super(host);
@@ -82,6 +84,7 @@
 
     @Override
     protected void handleClick() {
+        super.handleClick();
         sendIntent("click", mOnClick, mOnClickUri);
     }
 
@@ -133,6 +136,17 @@
         mOnClickUri = intent.getStringExtra("onClickUri");
         mOnLongClick = intent.getParcelableExtra("onLongClick");
         mOnLongClickUri = intent.getStringExtra("onLongClickUri");
+        mIntentPackage = intent.getStringExtra("package");
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsLogger.QS_INTENT;
+    }
+
+    @Override
+    protected String getMetricsPackage() {
+        return mIntentPackage == null ? "" : mIntentPackage;
     }
 
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index 11ec722..ab22ada 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.qs.tiles;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
@@ -58,6 +59,7 @@
 
     @Override
     protected void handleClick() {
+        super.handleClick();
         final boolean wasEnabled = (Boolean) mState.value;
         mController.setLocationEnabled(!wasEnabled);
         mEnable.setAllowAnimation(true);
@@ -87,6 +89,11 @@
     }
 
     @Override
+    public int getMetricsCategory() {
+        return MetricsLogger.QS_LOCATION;
+    }
+
+    @Override
     protected String composeChangeAnnouncement() {
         if (mState.value) {
             return mContext.getString(R.string.accessibility_quick_settings_location_changed_on);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index f46b9a6..7e3fe76 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -18,6 +18,7 @@
 
 import android.content.res.Configuration;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.statusbar.policy.RotationLockController;
@@ -58,6 +59,7 @@
 
     @Override
     protected void handleClick() {
+        super.handleClick();
         if (mController == null) return;
         final boolean newState = !mState.value;
         mController.setRotationLocked(newState);
@@ -92,6 +94,11 @@
                 R.string.accessibility_rotation_lock_off);
     }
 
+    @Override
+    public int getMetricsCategory() {
+        return MetricsLogger.QS_ROTATIONLOCK;
+    }
+
     /**
      * Get the correct accessibility string based on the state
      *
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index d589366..228c293 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -26,6 +26,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.settingslib.wifi.AccessPoint;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSDetailItems;
@@ -93,6 +94,7 @@
 
     @Override
     protected void handleClick() {
+        super.handleClick();
         mState.copyTo(mStateBeforeClick);
         mController.setWifiEnabled(!mState.enabled);
     }
@@ -159,6 +161,11 @@
     }
 
     @Override
+    public int getMetricsCategory() {
+        return MetricsLogger.QS_WIFI;
+    }
+
+    @Override
     protected boolean shouldAnnouncementBeDelayed() {
         return mStateBeforeClick.enabled == mState.enabled;
     }
@@ -274,6 +281,11 @@
         }
 
         @Override
+        public int getMetricsCategory() {
+            return MetricsLogger.QS_WIFI_DETAILS;
+        }
+
+        @Override
         public View createDetailView(Context context, View convertView, ViewGroup parent) {
             if (DEBUG) Log.d(TAG, "createDetailView convertView=" + (convertView != null));
             mAccessPoints = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 194bcfa..ad27c6e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -43,6 +43,7 @@
 import android.view.ViewGroup;
 import android.widget.BaseAdapter;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.internal.util.UserIcons;
 import com.android.systemui.BitmapHelper;
 import com.android.systemui.GuestResumeSessionReceiver;
@@ -548,6 +549,11 @@
         @Override
         public void setToggleState(boolean state) {
         }
+
+        @Override
+        public int getMetricsCategory() {
+            return MetricsLogger.QS_USERDETAIL;
+        }
     };
 
     private final KeyguardMonitor.Callback mCallback = new KeyguardMonitor.Callback() {
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index 58d0fce..a49fb76 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -1857,7 +1857,7 @@
                jintArray limits)
 {
     if (kLogApi) {
-        ALOGD("nScriptForEach, con(%p), s(%p), slot(%i) ains(%p) aout(%lli)", (RsContext)con, (void *)script, slot, ains, aout);
+        ALOGD("nScriptForEach, con(%p), s(%p), slot(%i) ains(%p) aout(%" PRId64 ")", (RsContext)con, (void *)script, slot, ains, aout);
     }
 
     jint   in_len = 0;
diff --git a/services/core/java/com/android/server/AssetAtlasService.java b/services/core/java/com/android/server/AssetAtlasService.java
index 26f4232..ebc810f 100644
--- a/services/core/java/com/android/server/AssetAtlasService.java
+++ b/services/core/java/com/android/server/AssetAtlasService.java
@@ -119,7 +119,6 @@
     // long0: SkBitmap*, the native bitmap object
     // long1: x position
     // long2: y position
-    // long3: rotated, 1 if the bitmap must be rotated, 0 otherwise
     private long[] mAtlasMap;
 
     /**
@@ -236,7 +235,7 @@
         /**
          * Renders a list of bitmaps into the atlas. The position of each bitmap
          * was decided by the packing algorithm and will be honored by this
-         * method. If need be this method will also rotate bitmaps.
+         * method.
          *
          * @param buffer The buffer to render the atlas entries into
          * @param atlas The atlas to pack the bitmaps into
@@ -280,16 +279,11 @@
 
                     canvas.save();
                     canvas.translate(entry.x, entry.y);
-                    if (entry.rotated) {
-                        canvas.translate(bitmap.getHeight(), 0.0f);
-                        canvas.rotate(90.0f);
-                    }
                     canvas.drawBitmap(bitmap, 0.0f, 0.0f, null);
                     canvas.restore();
                     atlasMap[mapIndex++] = bitmap.refSkPixelRef();
                     atlasMap[mapIndex++] = entry.x;
                     atlasMap[mapIndex++] = entry.y;
-                    atlasMap[mapIndex++] = entry.rotated ? 1 : 0;
                 }
             }
 
diff --git a/services/core/java/com/android/server/policy/GlobalActions.java b/services/core/java/com/android/server/policy/GlobalActions.java
index b431b33..3cee927 100644
--- a/services/core/java/com/android/server/policy/GlobalActions.java
+++ b/services/core/java/com/android/server/policy/GlobalActions.java
@@ -1138,7 +1138,7 @@
 
         public GlobalActionsDialog(Context context, AlertParams params) {
             super(context, getDialogTheme(context));
-            mContext = context;
+            mContext = getContext();
             mAlert = new AlertController(mContext, this, getWindow());
             mAdapter = (MyAdapter) params.mAdapter;
             mWindowTouchSlop = ViewConfiguration.get(context).getScaledWindowTouchSlop();
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 2d265e2..925a609 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -28,6 +28,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
+import android.content.res.Resources.Theme;
 import android.os.Build;
 import android.os.Environment;
 import android.os.FactoryTest;
@@ -291,7 +292,7 @@
     private void createSystemContext() {
         ActivityThread activityThread = ActivityThread.systemMain();
         mSystemContext = activityThread.getSystemContext();
-        mSystemContext.setTheme(android.R.style.Theme_DeviceDefault_Light_DarkActionBar);
+        mSystemContext.setTheme(android.R.style.Theme_Material_DayNight_DarkActionBar);
     }
 
     /**
@@ -1026,6 +1027,12 @@
         w.getDefaultDisplay().getMetrics(metrics);
         context.getResources().updateConfiguration(config, metrics);
 
+        // The system context's theme may be configuration-dependent.
+        final Theme systemTheme = context.getTheme();
+        if (systemTheme.getChangingConfigurations() != 0) {
+            systemTheme.rebase();
+        }
+
         try {
             // TODO: use boot phase
             mPowerManagerService.systemReady(mActivityManagerService.getAppOpsService());
diff --git a/telecomm/java/android/telecom/DefaultDialerManager.java b/telecomm/java/android/telecom/DefaultDialerManager.java
index fd0c06d..d3df151 100644
--- a/telecomm/java/android/telecom/DefaultDialerManager.java
+++ b/telecomm/java/android/telecom/DefaultDialerManager.java
@@ -20,6 +20,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.net.Uri;
 import android.provider.Settings;
 import android.text.TextUtils;
 
@@ -151,14 +152,14 @@
 
         for (ResolveInfo resolveInfo : resolveInfoList) {
             final ActivityInfo activityInfo = resolveInfo.activityInfo;
-            if (activityInfo == null) {
-                continue;
+            if (activityInfo != null && !packageNames.contains(activityInfo.packageName)) {
+                packageNames.add(activityInfo.packageName);
             }
-            packageNames.add(activityInfo.packageName);
         }
 
-        // TODO: Filter for apps that don't handle DIAL intent with tel scheme
-        return packageNames;
+        final Intent dialIntentWithTelScheme = new Intent(Intent.ACTION_DIAL);
+        dialIntentWithTelScheme.setData(Uri.fromParts(PhoneAccount.SCHEME_TEL, "", null));
+        return filterByIntent(context, packageNames, dialIntentWithTelScheme);
     }
 
     /**
@@ -182,6 +183,36 @@
                 || packageName.equals(tm.getSystemDialerPackage());
     }
 
+    /**
+     * Filter a given list of package names for those packages that contain an activity that has
+     * an intent filter for a given intent.
+     *
+     * @param context A valid context
+     * @param packageNames List of package names to filter.
+     * @return The filtered list.
+     */
+    private static List<String> filterByIntent(Context context, List<String> packageNames,
+            Intent intent) {
+        if (packageNames == null || packageNames.isEmpty()) {
+            return new ArrayList<>();
+        }
+
+        final List<String> result = new ArrayList<>();
+        final List<ResolveInfo> resolveInfoList =
+                context.getPackageManager().queryIntentActivities(intent, 0);
+        final int length = resolveInfoList.size();
+        for (int i = 0; i < length; i++) {
+            final ActivityInfo info = resolveInfoList.get(i).activityInfo;
+            if (info != null && packageNames.contains(info.packageName)
+                    && !result.contains(info.packageName)) {
+                result.add(info.packageName);
+            }
+        }
+
+        return result;
+    }
+
+
     private static TelecomManager getTelecomManager(Context context) {
         return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
     }
diff --git a/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java b/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java
index dd823ae..b54f9be 100644
--- a/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java
+++ b/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java
@@ -30,6 +30,7 @@
 
 import junit.framework.Assert;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
@@ -102,7 +103,11 @@
             // otherwise raise an
             // exception with the first error encountered.
             assertNull(getStackTrace(err), err);
-            assertTrue("App crashed after launch.", processStillUp(packageName));
+            try {
+                assertTrue("App crashed after launch.", processStillUp(packageName));
+            } finally {
+                returnHome();
+            }
         } else {
             Log.d(TAG, "Missing argument, use " + PACKAGE_TO_LAUNCH +
                     " to specify the package to launch");
@@ -138,6 +143,19 @@
         }
     }
 
+    private void returnHome() {
+        Intent homeIntent = new Intent(Intent.ACTION_MAIN);
+        homeIntent.addCategory(Intent.CATEGORY_HOME);
+        homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        // Send the "home" intent and wait 2 seconds for us to get there
+        mContext.startActivity(homeIntent);
+        try {
+            Thread.sleep(mWorkspaceLaunchTimeout);
+        } catch (InterruptedException e) {
+            // ignore
+        }
+    }
+
     /**
      * Launches and activity and queries for errors.
      *
@@ -150,9 +168,6 @@
         // the recommended way to see if this is a tv or not.
         boolean isleanback = !mPackageManager.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)
             && !mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
-        Intent homeIntent = new Intent(Intent.ACTION_MAIN);
-        homeIntent.addCategory(Intent.CATEGORY_HOME);
-        homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         Intent intent;
         if (isleanback) {
             Log.d(TAG, "Leanback and relax! " + packageName);
@@ -173,14 +188,6 @@
             // ignore
         }
 
-        // Send the "home" intent and wait 2 seconds for us to get there
-        mContext.startActivity(homeIntent);
-        try {
-            Thread.sleep(mWorkspaceLaunchTimeout);
-        } catch (InterruptedException e) {
-            // ignore
-        }
-
         // See if there are any errors. We wait until down here to give ANRs as
         // much time as
         // possible to occur.
@@ -198,6 +205,12 @@
         return null;
     }
 
+    private boolean ensureForegroundActivity(RunningAppProcessInfo info) {
+        Log.d(TAG, String.format("ensureForegroundActivity: proc=%s, pid=%d, state=%d",
+                info.processName, info.pid, info.processState));
+        return info.processState == ActivityManager.PROCESS_STATE_TOP;
+    }
+
     /**
      * Determine if a given package is still running.
      *
@@ -207,19 +220,32 @@
     private boolean processStillUp(String packageName) {
         String processName = getProcessName(packageName);
         List<RunningAppProcessInfo> runningApps = mActivityManager.getRunningAppProcesses();
+        List<RunningAppProcessInfo> relatedProcs = new ArrayList<>();
         for (RunningAppProcessInfo app : runningApps) {
             if (app.processName.equalsIgnoreCase(processName)) {
-                Log.d(TAG, "Found process " + app.processName);
+                if (!ensureForegroundActivity(app)) {
+                    Log.w(TAG, "Found process but it's not top activity.");
+                    return false;
+                }
                 return true;
             }
             for (String relatedPackage : app.pkgList) {
-                if (relatedPackage.equalsIgnoreCase(processName)) {
-                    Log.d(TAG, "Found process " + app.processName);
-                    return true;
+                if (relatedPackage.equalsIgnoreCase(packageName)) {
+                    relatedProcs.add(app);
                 }
             }
         }
-        Log.d(TAG, "Failed to find process " + processName + " with package name "
+        // now that we are here, we've found no RAPI's directly matching processName, but
+        // potentially a List of them with one of related packages being processName
+        if (!relatedProcs.isEmpty()) {
+            for (RunningAppProcessInfo app : relatedProcs) {
+                if (ensureForegroundActivity(app)) {
+                    return true;
+                }
+            }
+            Log.w(TAG, "Found related processes, but none has top activity.");
+        }
+        Log.w(TAG, "Failed to find process " + processName + " with package name "
                 + packageName);
         return false;
     }