merge in jb-mr2-release history after reset to jb-mr2-dev
diff --git a/api/current.txt b/api/current.txt
index f4407b5..f2fcbd6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -24953,6 +24953,14 @@
     field public static final int ORIENTATION_UNKNOWN = -1; // 0xffffffff
   }
 
+  public abstract interface Overlay {
+    method public abstract void add(android.graphics.drawable.Drawable);
+    method public abstract void add(android.view.View);
+    method public abstract void clear();
+    method public abstract void remove(android.graphics.drawable.Drawable);
+    method public abstract void remove(android.view.View);
+  }
+
   public class ScaleGestureDetector {
     ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener);
     method public float getCurrentSpan();
@@ -25257,6 +25265,7 @@
     method public int getNextFocusUpId();
     method public android.view.View.OnFocusChangeListener getOnFocusChangeListener();
     method public int getOverScrollMode();
+    method public android.view.Overlay getOverlay();
     method public int getPaddingBottom();
     method public int getPaddingEnd();
     method public int getPaddingLeft();
diff --git a/cmds/svc/src/com/android/commands/svc/PowerCommand.java b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
index 58105fa..da8586c 100644
--- a/cmds/svc/src/com/android/commands/svc/PowerCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
@@ -36,12 +36,18 @@
         return shortHelp() + "\n"
                 + "\n"
                 + "usage: svc power stayon [true|false|usb|ac|wireless]\n"
-                + "         Set the 'keep awake while plugged in' setting.\n";
+                + "         Set the 'keep awake while plugged in' setting.\n"
+                + "       svc power reboot [reason]\n"
+                + "         Perform a runtime shutdown and reboot device with specified reason.\n"
+                + "       svc power shutdown\n"
+                + "         Perform a runtime shutdown and power off the device.\n";
     }
 
     public void run(String[] args) {
         fail: {
             if (args.length >= 2) {
+                IPowerManager pm = IPowerManager.Stub.asInterface(
+                        ServiceManager.getService(Context.POWER_SERVICE));
                 if ("stayon".equals(args[1]) && args.length == 3) {
                     int val;
                     if ("true".equals(args[2])) {
@@ -60,8 +66,6 @@
                     } else {
                         break fail;
                     }
-                    IPowerManager pm
-                            = IPowerManager.Stub.asInterface(ServiceManager.getService(Context.POWER_SERVICE));
                     try {
                         if (val != 0) {
                             // if the request is not to set it to false, wake up the screen so that
@@ -74,6 +78,26 @@
                         System.err.println("Faild to set setting: " + e);
                     }
                     return;
+                } else if ("reboot".equals(args[1])) {
+                    String mode = null;
+                    if (args.length == 3) {
+                        mode = args[2];
+                    }
+                    try {
+                        // no confirm, wait till device is rebooted
+                        pm.reboot(false, mode, true);
+                    } catch (RemoteException e) {
+                        System.err.println("Failed to reboot.");
+                    }
+                    return;
+                } else if ("shutdown".equals(args[1])) {
+                    try {
+                        // no confirm, wait till device is off
+                        pm.shutdown(false, true);
+                    } catch (RemoteException e) {
+                        System.err.println("Failed to shutdown.");
+                    }
+                    return;
                 }
             }
         }
diff --git a/core/java/android/view/Overlay.java b/core/java/android/view/Overlay.java
new file mode 100644
index 0000000..f15d4d2
--- /dev/null
+++ b/core/java/android/view/Overlay.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2013 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.view;
+
+import android.graphics.drawable.Drawable;
+
+/**
+ * An overlay is an extra layer that sits on top of a View (the "host view") which is drawn after
+ * all other content in that view (including children, if the view is a ViewGroup). Interaction
+ * with the overlay layer is done in terms of adding/removing views and drawables. Invalidation and
+ * redrawing of the overlay layer (and its host view) is handled differently for views versus
+ * drawables in the overlay. Views invalidate themselves as usual, causing appropriate redrawing
+ * to occur automatically. Drawables, on the other hand, do not manage invalidation, so changes to
+ * drawable objects should be accompanied by appropriate calls to invalidate() on the host view.
+ *
+ * @see android.view.View#getOverlay()
+ */
+public interface Overlay {
+
+    /**
+     * Adds a Drawable to the overlay. The bounds of the drawable should be relative to
+     * the host view. Any drawable added to the overlay should be removed when it is no longer
+     * needed or no longer visible. There is no automatic invalidation of the host view; changes to
+     * the drawable should be accompanied by appropriate invalidation calls to the host view
+     * to cause the proper area of the view, and the overlay, to be redrawn.
+     *
+     * @param drawable The Drawable to be added to the overlay. This drawable will be
+     * drawn when the view redraws its overlay.
+     * @see #remove(android.graphics.drawable.Drawable)
+     * @see #add(View)
+     */
+    void add(Drawable drawable);
+
+    /**
+     * Removes the specified Drawable from the overlay.
+     *
+     * @param drawable The Drawable to be removed from the overlay.
+     * @see #add(android.graphics.drawable.Drawable)
+     */
+    void remove(Drawable drawable);
+
+    /**
+     * Adds a View to the overlay. The bounds of the added view should be relative to
+     * the host view. Any view added to the overlay should be removed when it is no longer
+     * needed or no longer visible. The view must not be parented elsewhere when it is added
+     * to the overlay.
+     *
+     * @param view The View to be added to the overlay. The added view will be
+     * drawn when the overlay is drawn.
+     * @see #remove(View)
+     * @see #add(android.graphics.drawable.Drawable)
+     */
+    void add(View view);
+
+    /**
+     * Removes the specified View from the overlay.
+     *
+     * @param view The View to be removed from the overlay.
+     * @see #add(View)
+     */
+    void remove(View view);
+
+    /**
+     * Removes all views and drawables from the overlay.
+     */
+    void clear();
+}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index df07dcd..e869d09 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -574,7 +574,8 @@
      * @param maxLayer The highest (top-most Z order) surface layer to
      * include in the screenshot.
      * @return Returns a Bitmap containing the screen contents, or null
-     * if an error occurs.
+     * if an error occurs. Make sure to call Bitmap.recycle() as soon as
+     * possible, once its content is not needed anymore.
      */
     public static Bitmap screenshot(int width, int height, int minLayer, int maxLayer) {
         // TODO: should take the display as a parameter
@@ -586,6 +587,14 @@
     /**
      * Like {@link SurfaceControl#screenshot(int, int, int, int)} but includes all
      * Surfaces in the screenshot.
+     *
+     * @param width The desired width of the returned bitmap; the raw
+     * screen will be scaled down to this size.
+     * @param height The desired height of the returned bitmap; the raw
+     * screen will be scaled down to this size.
+     * @return Returns a Bitmap containing the screen contents, or null
+     * if an error occurs. Make sure to call Bitmap.recycle() as soon as
+     * possible, once its content is not needed anymore.
      */
     public static Bitmap screenshot(int width, int height) {
         // TODO: should take the display as a parameter
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d412d82..2fa9484 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3223,6 +3223,12 @@
     AccessibilityDelegate mAccessibilityDelegate;
 
     /**
+     * The view's overlay layer. Developers get a reference to the overlay via getOverlay()
+     * and add/remove objects to/from the overlay directly through the Overlay methods.
+     */
+    ViewOverlay mOverlay;
+
+    /**
      * Consistency verifier for debugging purposes.
      * @hide
      */
@@ -6069,8 +6075,7 @@
             mTransientStateCount = 0;
             Log.e(VIEW_LOG_TAG, "hasTransientState decremented below 0: " +
                     "unmatched pair of setHasTransientState calls");
-        }
-        if ((hasTransientState && mTransientStateCount == 1) ||
+        } else if ((hasTransientState && mTransientStateCount == 1) ||
                 (!hasTransientState && mTransientStateCount == 0)) {
             // update flag if we've just incremented up from 0 or decremented down to 0
             mPrivateFlags2 = (mPrivateFlags2 & ~PFLAG2_HAS_TRANSIENT_STATE) |
@@ -9590,7 +9595,7 @@
                 mDisplayList.setTop(mTop);
             }
 
-            onSizeChanged(width, mBottom - mTop, width, oldHeight);
+            sizeChange(width, mBottom - mTop, width, oldHeight);
 
             if (!matrixIsIdentity) {
                 if ((mPrivateFlags & PFLAG_PIVOT_EXPLICITLY_SET) == 0) {
@@ -9663,7 +9668,7 @@
                 mDisplayList.setBottom(mBottom);
             }
 
-            onSizeChanged(width, mBottom - mTop, width, oldHeight);
+            sizeChange(width, mBottom - mTop, width, oldHeight);
 
             if (!matrixIsIdentity) {
                 if ((mPrivateFlags & PFLAG_PIVOT_EXPLICITLY_SET) == 0) {
@@ -9730,7 +9735,7 @@
                 mDisplayList.setLeft(left);
             }
 
-            onSizeChanged(mRight - mLeft, height, oldWidth, height);
+            sizeChange(mRight - mLeft, height, oldWidth, height);
 
             if (!matrixIsIdentity) {
                 if ((mPrivateFlags & PFLAG_PIVOT_EXPLICITLY_SET) == 0) {
@@ -9794,7 +9799,7 @@
                 mDisplayList.setRight(mRight);
             }
 
-            onSizeChanged(mRight - mLeft, height, oldWidth, height);
+            sizeChange(mRight - mLeft, height, oldWidth, height);
 
             if (!matrixIsIdentity) {
                 if ((mPrivateFlags & PFLAG_PIVOT_EXPLICITLY_SET) == 0) {
@@ -12092,6 +12097,9 @@
     void dispatchAttachedToWindow(AttachInfo info, int visibility) {
         //System.out.println("Attached! " + this);
         mAttachInfo = info;
+        if (mOverlay != null) {
+            mOverlay.mAttachInfo = info;
+        }
         mWindowAttachCount++;
         // We will need to evaluate the drawable state at least once.
         mPrivateFlags |= PFLAG_DRAWABLE_STATE_DIRTY;
@@ -12160,6 +12168,9 @@
         }
 
         mAttachInfo = null;
+        if (mOverlay != null) {
+            mOverlay.mAttachInfo = null;
+        }
     }
 
     /**
@@ -12815,6 +12826,9 @@
                     // Fast path for layouts with no backgrounds
                     if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
                         dispatchDraw(canvas);
+                        if (mOverlay != null && !mOverlay.isEmpty()) {
+                            mOverlay.draw(canvas);
+                        }
                     } else {
                         draw(canvas);
                     }
@@ -13128,6 +13142,9 @@
             if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
                 mPrivateFlags &= ~PFLAG_DIRTY_MASK;
                 dispatchDraw(canvas);
+                if (mOverlay != null && !mOverlay.isEmpty()) {
+                    mOverlay.draw(canvas);
+                }
             } else {
                 draw(canvas);
             }
@@ -13883,6 +13900,10 @@
             // Step 6, draw decorations (scrollbars)
             onDrawScrollBars(canvas);
 
+            if (mOverlay != null && !mOverlay.isEmpty()) {
+                mOverlay.dispatchDraw(canvas);
+            }
+
             // we're done...
             return;
         }
@@ -14022,6 +14043,37 @@
 
         // Step 6, draw decorations (scrollbars)
         onDrawScrollBars(canvas);
+
+        if (mOverlay != null && !mOverlay.isEmpty()) {
+            mOverlay.dispatchDraw(canvas);
+        }
+    }
+
+    /**
+     * Called by the addToOverlay() methods to create, attach, and size the overlay as necessary
+     */
+    private void setupOverlay() {
+        if (mOverlay == null) {
+            mOverlay = new ViewOverlay(mContext, this);
+            mOverlay.mAttachInfo = mAttachInfo;
+            mOverlay.setRight(mRight);
+            mOverlay.setBottom(mBottom);
+        }
+    }
+
+    /**
+     * Returns the overlay for this view, creating it if it does not yet exist. Adding drawables
+     * and/or views to the overlay will cause them to be displayed whenever the view itself is
+     * redrawn. Objects in the overlay should be actively managed: remove them when they should
+     * not be displayed anymore and invalidate this view appropriately when overlay drawables
+     * change. The overlay will always have the same size as its host view.
+     *
+     * @return The Overlay object for this view.
+     * @see Overlay
+     */
+    public Overlay getOverlay() {
+        setupOverlay();
+        return mOverlay;
     }
 
     /**
@@ -14277,7 +14329,7 @@
                         mTransformationInfo.mMatrixDirty = true;
                     }
                 }
-                onSizeChanged(newWidth, newHeight, oldWidth, oldHeight);
+                sizeChange(newWidth, newHeight, oldWidth, oldHeight);
             }
 
             if ((mViewFlags & VISIBILITY_MASK) == VISIBLE) {
@@ -14301,6 +14353,14 @@
         return changed;
     }
 
+    private void sizeChange(int newWidth, int newHeight, int oldWidth, int oldHeight) {
+        onSizeChanged(newWidth, newHeight, oldWidth, oldHeight);
+        if (mOverlay != null) {
+            mOverlay.setRight(mRight);
+            mOverlay.setBottom(mBottom);
+        }
+    }
+
     /**
      * Finalize inflating a view from XML.  This is called as the last phase
      * of inflation, after all child views have been added.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 442dfdb..d63f7bc 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1869,7 +1869,7 @@
                     removePointersFromTouchTargets(idBitsToAssign);
 
                     final int childrenCount = mChildrenCount;
-                    if (childrenCount != 0) {
+                    if (childrenCount != 0 || mOverlay != null) {
                         // Find a child that can receive the event.
                         // Scan children from front to back.
                         final View[] children = mChildren;
@@ -1906,6 +1906,27 @@
                                 break;
                             }
                         }
+                        if (mOverlay != null && newTouchTarget == null) {
+                            // Check to see whether the overlay can handle the event
+                            final View child = mOverlay;
+                            if (canViewReceivePointerEvents(child) &&
+                                    isTransformedTouchPointInView(x, y, child, null)) {
+                                newTouchTarget = getTouchTarget(child);
+                                if (newTouchTarget != null) {
+                                    newTouchTarget.pointerIdBits |= idBitsToAssign;
+                                } else {
+                                    resetCancelNextUpFlag(child);
+                                    if (dispatchTransformedTouchEvent(ev, false, child,
+                                            idBitsToAssign)) {
+                                        mLastTouchDownTime = ev.getDownTime();
+                                        mLastTouchDownX = ev.getX();
+                                        mLastTouchDownY = ev.getY();
+                                        newTouchTarget = addTouchTarget(child, idBitsToAssign);
+                                        alreadyDispatchedToNewTouchTarget = true;
+                                    }
+                                }
+                            }
+                        }
                     }
 
                     if (newTouchTarget == null && mFirstTouchTarget != null) {
@@ -3022,6 +3043,13 @@
                 child.mRecreateDisplayList = false;
             }
         }
+        if (mOverlay != null) {
+            mOverlay.mRecreateDisplayList = (mOverlay.mPrivateFlags & PFLAG_INVALIDATED)
+                    == PFLAG_INVALIDATED;
+            mOverlay.mPrivateFlags &= ~PFLAG_INVALIDATED;
+            mOverlay.getDisplayList();
+            mOverlay.mRecreateDisplayList = false;
+        }
     }
 
     /**
diff --git a/core/java/android/view/ViewOverlay.java b/core/java/android/view/ViewOverlay.java
new file mode 100644
index 0000000..8c2ab9d
--- /dev/null
+++ b/core/java/android/view/ViewOverlay.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2013 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.view;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+
+import java.util.ArrayList;
+
+/**
+ * ViewOverlay is a container that View uses to host all objects (views and drawables) that
+ * are added to its "overlay", gotten through {@link View#getOverlay()}. Views and drawables are
+ * added to the overlay via the add/remove methods in this class. These views and drawables are
+ * then drawn whenever the view itself is drawn, after which it will draw its overlay (if it
+ * exists).
+ *
+ * Besides managing and drawing the list of drawables, this class serves two purposes:
+ * (1) it noops layout calls because children are absolutely positioned and
+ * (2) it forwards all invalidation calls to its host view. The invalidation redirect is
+ * necessary because the overlay is not a child of the host view and invalidation cannot
+ * therefore follow the normal path up through the parent hierarchy.
+ *
+ * @hide
+ */
+class ViewOverlay extends ViewGroup implements Overlay {
+
+    /**
+     * The View for which this is an overlay. Invalidations of the overlay are redirected to
+     * this host view.
+     */
+    View mHostView;
+
+    /**
+     * The set of drawables to draw when the overlay is rendered.
+     */
+    ArrayList<Drawable> mDrawables = null;
+
+    ViewOverlay(Context context, View host) {
+        super(context);
+        mHostView = host;
+        mParent = mHostView.getParent();
+    }
+
+    @Override
+    public void add(Drawable drawable) {
+        if (mDrawables == null) {
+            mDrawables = new ArrayList<Drawable>();
+        }
+        if (!mDrawables.contains(drawable)) {
+            // Make each drawable unique in the overlay; can't add it more than once
+            mDrawables.add(drawable);
+            invalidate(drawable.getBounds());
+        }
+    }
+
+    @Override
+    public void remove(Drawable drawable) {
+        if (mDrawables != null) {
+            mDrawables.remove(drawable);
+            invalidate(drawable.getBounds());
+        }
+    }
+
+    @Override
+    public void add(View child) {
+        super.addView(child);
+    }
+
+    @Override
+    public void remove(View view) {
+        super.removeView(view);
+    }
+
+    @Override
+    public void clear() {
+        removeAllViews();
+        mDrawables.clear();
+    }
+
+    boolean isEmpty() {
+        if (getChildCount() == 0 && (mDrawables == null || mDrawables.size() == 0)) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        super.dispatchDraw(canvas);
+        final int numDrawables = (mDrawables == null) ? 0 : mDrawables.size();
+        for (int i = 0; i < numDrawables; ++i) {
+            mDrawables.get(i).draw(canvas);
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        // Noop: children are positioned absolutely
+    }
+
+    /*
+     The following invalidation overrides exist for the purpose of redirecting invalidation to
+     the host view. The overlay is not parented to the host view (since a View cannot be a parent),
+     so the invalidation cannot proceed through the normal parent hierarchy.
+     There is a built-in assumption that the overlay exactly covers the host view, therefore
+     the invalidation rectangles received do not need to be adjusted when forwarded to
+     the host view.
+     */
+
+    @Override
+    public void invalidate(Rect dirty) {
+        super.invalidate(dirty);
+        if (mHostView != null) {
+            dirty.offset(getLeft(), getTop());
+            mHostView.invalidate(dirty);
+        }
+    }
+
+    @Override
+    public void invalidate(int l, int t, int r, int b) {
+        super.invalidate(l, t, r, b);
+        if (mHostView != null) {
+            mHostView.invalidate(l, t, r, b);
+        }
+    }
+
+    @Override
+    public void invalidate() {
+        super.invalidate();
+        if (mHostView != null) {
+            mHostView.invalidate();
+        }
+    }
+
+    @Override
+    void invalidate(boolean invalidateCache) {
+        super.invalidate(invalidateCache);
+        if (mHostView != null) {
+            mHostView.invalidate(invalidateCache);
+        }
+    }
+
+    @Override
+    void invalidateViewProperty(boolean invalidateParent, boolean forceRedraw) {
+        super.invalidateViewProperty(invalidateParent, forceRedraw);
+        if (mHostView != null) {
+            mHostView.invalidateViewProperty(invalidateParent, forceRedraw);
+        }
+    }
+
+    @Override
+    protected void invalidateParentCaches() {
+        super.invalidateParentCaches();
+        if (mHostView != null) {
+            mHostView.invalidateParentCaches();
+        }
+    }
+
+    @Override
+    protected void invalidateParentIfNeeded() {
+        super.invalidateParentIfNeeded();
+        if (mHostView != null) {
+            mHostView.invalidateParentIfNeeded();
+        }
+    }
+
+    public void invalidateChildFast(View child, final Rect dirty) {
+        if (mHostView != null) {
+            // Note: This is not a "fast" invalidation. Would be nice to instead invalidate using DL
+            // properties and a dirty rect instead of causing a real invalidation of the host view
+            int left = child.mLeft;
+            int top = child.mTop;
+            if (!child.getMatrix().isIdentity()) {
+                child.transformRect(dirty);
+            }
+            dirty.offset(left, top);
+            mHostView.invalidate(dirty);
+        }
+    }
+
+    @Override
+    public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
+        if (mHostView != null) {
+            mHostView.invalidate(dirty);
+        }
+        return null;
+    }
+}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 396fd68..d659110 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -6167,6 +6167,7 @@
         private ArrayList<View> mSkippedScrap;
 
         private SparseArray<View> mTransientStateViews;
+        private LongSparseArray<View> mTransientStateViewsById;
 
         public void setViewTypeCount(int viewTypeCount) {
             if (viewTypeCount < 1) {
@@ -6205,6 +6206,12 @@
                     mTransientStateViews.valueAt(i).forceLayout();
                 }
             }
+            if (mTransientStateViewsById != null) {
+                final int count = mTransientStateViewsById.size();
+                for (int i = 0; i < count; i++) {
+                    mTransientStateViewsById.valueAt(i).forceLayout();
+                }
+            }
         }
 
         public boolean shouldRecycleViewType(int viewType) {
@@ -6234,6 +6241,9 @@
             if (mTransientStateViews != null) {
                 mTransientStateViews.clear();
             }
+            if (mTransientStateViewsById != null) {
+                mTransientStateViewsById.clear();
+            }
         }
 
         /**
@@ -6281,16 +6291,21 @@
         }
 
         View getTransientStateView(int position) {
-            if (mTransientStateViews == null) {
-                return null;
+            if (mAdapter != null && mAdapterHasStableIds && mTransientStateViewsById != null) {
+                long id = mAdapter.getItemId(position);
+                View result = mTransientStateViewsById.get(id);
+                mTransientStateViewsById.remove(id);
+                return result;
             }
-            final int index = mTransientStateViews.indexOfKey(position);
-            if (index < 0) {
-                return null;
+            if (mTransientStateViews != null) {
+                final int index = mTransientStateViews.indexOfKey(position);
+                if (index >= 0) {
+                    View result = mTransientStateViews.valueAt(index);
+                    mTransientStateViews.removeAt(index);
+                    return result;
+                }
             }
-            final View result = mTransientStateViews.valueAt(index);
-            mTransientStateViews.removeAt(index);
-            return result;
+            return null;
         }
 
         /**
@@ -6300,6 +6315,9 @@
             if (mTransientStateViews != null) {
                 mTransientStateViews.clear();
             }
+            if (mTransientStateViewsById != null) {
+                mTransientStateViewsById.clear();
+            }
         }
 
         /**
@@ -6342,11 +6360,18 @@
                     mSkippedScrap.add(scrap);
                 }
                 if (scrapHasTransientState) {
-                    if (mTransientStateViews == null) {
-                        mTransientStateViews = new SparseArray<View>();
-                    }
                     scrap.dispatchStartTemporaryDetach();
-                    mTransientStateViews.put(position, scrap);
+                    if (mAdapter != null && mAdapterHasStableIds) {
+                        if (mTransientStateViewsById == null) {
+                            mTransientStateViewsById = new LongSparseArray<View>();
+                        }
+                        mTransientStateViewsById.put(lp.itemId, scrap);
+                    } else {
+                        if (mTransientStateViews == null) {
+                            mTransientStateViews = new SparseArray<View>();
+                        }
+                        mTransientStateViews.put(position, scrap);
+                    }
                 }
                 return;
             }
@@ -6405,10 +6430,18 @@
                             removeDetachedView(victim, false);
                         }
                         if (scrapHasTransientState) {
-                            if (mTransientStateViews == null) {
-                                mTransientStateViews = new SparseArray<View>();
+                            if (mAdapter != null && mAdapterHasStableIds) {
+                                if (mTransientStateViewsById == null) {
+                                    mTransientStateViewsById = new LongSparseArray<View>();
+                                }
+                                long id = mAdapter.getItemId(mFirstActivePosition + i);
+                                mTransientStateViewsById.put(id, victim);
+                            } else {
+                                if (mTransientStateViews == null) {
+                                    mTransientStateViews = new SparseArray<View>();
+                                }
+                                mTransientStateViews.put(mFirstActivePosition + i, victim);
                             }
-                            mTransientStateViews.put(mFirstActivePosition + i, victim);
                         }
                         continue;
                     }
@@ -6457,6 +6490,15 @@
                     }
                 }
             }
+            if (mTransientStateViewsById != null) {
+                for (int i = 0; i < mTransientStateViewsById.size(); i++) {
+                    final View v = mTransientStateViewsById.valueAt(i);
+                    if (!v.hasTransientState()) {
+                        mTransientStateViewsById.removeAt(i);
+                        i--;
+                    }
+                }
+            }
         }
 
         /**
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 69e3177..7c40a64 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -1550,6 +1550,32 @@
 
             setSelectedPositionInt(mNextSelectedPosition);
 
+            // Remember which child, if any, had accessibility focus. This must
+            // occur before recycling any views, since that will clear
+            // accessibility focus.
+            final ViewRootImpl viewRootImpl = getViewRootImpl();
+            if (viewRootImpl != null) {
+                final View accessFocusedView = viewRootImpl.getAccessibilityFocusedHost();
+                if (accessFocusedView != null) {
+                    final View accessFocusedChild = findAccessibilityFocusedChild(
+                            accessFocusedView);
+                    if (accessFocusedChild != null) {
+                        if (!dataChanged || isDirectChildHeaderOrFooter(accessFocusedChild)) {
+                            // If the views won't be changing, try to maintain
+                            // focus on the current view host and (if
+                            // applicable) its virtual view.
+                            accessibilityFocusLayoutRestoreView = accessFocusedView;
+                            accessibilityFocusLayoutRestoreNode = viewRootImpl
+                                    .getAccessibilityFocusedVirtualView();
+                        } else {
+                            // Otherwise, try to maintain focus at the same
+                            // position.
+                            accessibilityFocusPosition = getPositionForView(accessFocusedChild);
+                        }
+                    }
+                }
+            }
+
             // Pull all children into the RecycleBin.
             // These views will be reused if possible
             final int firstPosition = mFirstPosition;
@@ -1590,30 +1616,6 @@
                 requestFocus();
             }
 
-            // Remember which child, if any, had accessibility focus.
-            final ViewRootImpl viewRootImpl = getViewRootImpl();
-            if (viewRootImpl != null) {
-                final View accessFocusedView = viewRootImpl.getAccessibilityFocusedHost();
-                if (accessFocusedView != null) {
-                    final View accessFocusedChild = findAccessibilityFocusedChild(
-                            accessFocusedView);
-                    if (accessFocusedChild != null) {
-                        if (!dataChanged || isDirectChildHeaderOrFooter(accessFocusedChild)) {
-                            // If the views won't be changing, try to maintain
-                            // focus on the current view host and (if
-                            // applicable) its virtual view.
-                            accessibilityFocusLayoutRestoreView = accessFocusedView;
-                            accessibilityFocusLayoutRestoreNode = viewRootImpl
-                                    .getAccessibilityFocusedVirtualView();
-                        } else {
-                            // Otherwise, try to maintain focus at the same
-                            // position.
-                            accessibilityFocusPosition = getPositionForView(accessFocusedChild);
-                        }
-                    }
-                }
-            }
-
             // Clear out old views
             detachAllViewsFromParent();
             recycleBin.removeSkippedScrap();
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index aef7bdc..102815f 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -885,7 +885,7 @@
     <string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"Umožňuje aplikaci číst historii všech adres URL navštívených v Prohlížeči a všechny záložky v Prohlížeči. Poznámka: Pro prohlížeče třetí strany a jiné aplikace umožňující procházení webu toto oprávnění platit nemusí."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="3714785165273314490">"psaní webových záložek a historie"</string>
     <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"Umožňuje aplikaci upravit historii prohlížeče nebo záložky uložené v tabletu. Aplikace s tímto oprávněním může vymazat či pozměnit data prohlížeče. Poznámka: Pro prohlížeče třetí strany a jiné aplikace umožňující procházení webu toto oprávnění platit nemusí."</string>
-    <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Umožňuje aplikaci upravit historii prohlížeče nebo záložky uložené v telefonu. Aplikace s tímto oprávněním může vymazat či pozměnit data prohlížeče .Poznámka: Pro prohlížeče třetí strany a jiné aplikace umožňující procházení webu toto oprávnění platit nemusí."</string>
+    <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Umožňuje aplikaci upravit historii prohlížeče nebo záložky uložené v telefonu. Aplikace s tímto oprávněním může vymazat či pozměnit data prohlížeče. Poznámka: Pro prohlížeče třetí strany a jiné aplikace umožňující procházení webu toto oprávnění platit nemusí."</string>
     <string name="permlab_setAlarm" msgid="1379294556362091814">"nastavení budíku"</string>
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Umožňuje aplikaci nastavit budík v nainstalované aplikaci budík. Některé aplikace budík tuto funkci nemusí obsahovat."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"přidat hlasovou zprávu"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 1fc59b6..eeb6878 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -329,7 +329,7 @@
     <string name="permdesc_confirm_full_backup" msgid="1748762171637699562">"Permite que la aplicación inicie la IU de confirmación de copia de seguridad completa. No todas las aplicaciones pueden utilizar este permiso."</string>
     <string name="permlab_internalSystemWindow" msgid="2148563628140193231">"mostrar ventanas no autorizadas"</string>
     <string name="permdesc_internalSystemWindow" msgid="7458387759461466397">"Permite que la aplicación cree ventanas para la interfaz de usuario interna del sistema. Las aplicaciones normales no deben usar este permiso."</string>
-    <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"destacar sobre otras aplicaciones"</string>
+    <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"mostrar sobre otras aplicaciones"</string>
     <string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"Permite a la aplicación escribir por encima de otras aplicaciones o partes de la interfaz de usuario que pueden interferir en tu uso de la interfaz en cualquier aplicación o cambiar lo que crees que ves en otras aplicaciones."</string>
     <string name="permlab_setAnimationScale" msgid="2805103241153907174">"modificar la velocidad de la animación global"</string>
     <string name="permdesc_setAnimationScale" msgid="7690063428924343571">"Permite que la aplicación cambie la velocidad de animación global (animaciones más rápidas o más lentas) en cualquier momento."</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 98d7477..8aedc15 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1188,7 +1188,7 @@
     <string name="dlg_confirm_kill_storage_users_text" msgid="5100428757107469454">"Po włączeniu nośnika USB niektóre używane aplikacje zostaną zatrzymane i mogą być niedostępne do chwili jego wyłączenia."</string>
     <string name="dlg_error_title" msgid="7323658469626514207">"Operacja USB zakończona niepowodzeniem"</string>
     <string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
-    <string name="usb_mtp_notification_title" msgid="3699913097391550394">"Podłączono jako urządzenie multimedialne."</string>
+    <string name="usb_mtp_notification_title" msgid="3699913097391550394">"Podłączono urządzenie multimedialne"</string>
     <string name="usb_ptp_notification_title" msgid="1960817192216064833">"Podłączono jako aparat."</string>
     <string name="usb_cd_installer_notification_title" msgid="6774712827892090754">"Podłączono jako nośnik instalacyjny."</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Podłączono akcesorium USB"</string>
@@ -1320,7 +1320,7 @@
     <string name="sync_really_delete" msgid="2572600103122596243">"Usuń elementy."</string>
     <string name="sync_undo_deletes" msgid="2941317360600338602">"Cofnij usunięcia"</string>
     <string name="sync_do_nothing" msgid="3743764740430821845">"Nie wykonuj teraz żadnych czynności."</string>
-    <string name="choose_account_label" msgid="5655203089746423927">"Wybierz konto."</string>
+    <string name="choose_account_label" msgid="5655203089746423927">"Wybierz konto"</string>
     <string name="add_account_label" msgid="2935267344849993553">"Dodaj konto"</string>
     <string name="add_account_button_label" msgid="3611982894853435874">"Dodaj konto"</string>
     <string name="number_picker_increment_button" msgid="2412072272832284313">"Zwiększ"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 88c56bc..4fb58b8 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -40,7 +40,7 @@
     <string name="serviceEnabledFor" msgid="6856228140453471041">"Serviciul a fost activat pentru:"</string>
     <string name="serviceDisabled" msgid="1937553226592516411">"Serviciul a fost dezactivat."</string>
     <string name="serviceRegistered" msgid="6275019082598102493">"Înregistrarea a reuşit."</string>
-    <string name="serviceErased" msgid="1288584695297200972">"Ştergerea a reuşit."</string>
+    <string name="serviceErased" msgid="1288584695297200972">"Ștergerea a reuşit."</string>
     <string name="passwordIncorrect" msgid="7612208839450128715">"Parolă incorectă."</string>
     <string name="mmiComplete" msgid="8232527495411698359">"MMI finalizat."</string>
     <string name="badPin" msgid="9015277645546710014">"Codul PIN vechi introdus nu este corect."</string>
@@ -129,8 +129,8 @@
     <string name="contentServiceSync" msgid="8353523060269335667">"Sincronizare"</string>
     <string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Sincronizare"</string>
     <string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Prea multe ştergeri <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
-    <string name="low_memory" product="tablet" msgid="6494019234102154896">"Stocarea pe tabletă este plină. Ştergeţi câteva fişiere pentru a elibera spaţiu."</string>
-    <string name="low_memory" product="default" msgid="3475999286680000541">"Stocarea pe telefon este plină. Ştergeţi câteva fişiere pentru a elibera spaţiu."</string>
+    <string name="low_memory" product="tablet" msgid="6494019234102154896">"Stocarea pe tabletă este plină. Ștergeţi câteva fişiere pentru a elibera spaţiu."</string>
+    <string name="low_memory" product="default" msgid="3475999286680000541">"Stocarea pe telefon este plină. Ștergeţi câteva fişiere pentru a elibera spaţiu."</string>
     <string name="me" msgid="6545696007631404292">"Eu"</string>
     <string name="power_dialog" product="tablet" msgid="8545351420865202853">"Opţiuni tablet PC"</string>
     <string name="power_dialog" product="default" msgid="1319919075463988638">"Opţiuni telefon"</string>
@@ -628,9 +628,9 @@
     <string name="policydesc_resetPassword" msgid="605963962301904458">"Editaţi parola de deblocare a ecranului."</string>
     <string name="policylab_forceLock" msgid="2274085384704248431">"Blocaţi ecranul"</string>
     <string name="policydesc_forceLock" msgid="1141797588403827138">"Stabiliţi modul şi timpul în care se blochează ecranul."</string>
-    <string name="policylab_wipeData" msgid="3910545446758639713">"Ştergere integrală date"</string>
-    <string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"Ştergeţi datele de pe tabletă fără avertisment, efectuând resetarea configurării din fabrică."</string>
-    <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Ştergeţi datele din telefon fără avertisment, efectuând resetarea configurării din fabrică."</string>
+    <string name="policylab_wipeData" msgid="3910545446758639713">"Ștergere integrală date"</string>
+    <string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"Ștergeţi datele de pe tabletă fără avertisment, efectuând resetarea configurării din fabrică."</string>
+    <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Ștergeţi datele din telefon fără avertisment, efectuând resetarea configurării din fabrică."</string>
     <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Setaţi serverul proxy global pentru dispozitiv"</string>
     <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Setaţi serverul proxy global pentru dispozitiv care să fie utilizat cât timp politica este activă. Numai primul administrator al dispozitivului poate seta serverul proxy global activ."</string>
     <string name="policylab_expirePassword" msgid="885279151847254056">"Expirare parolă blocare ecran"</string>
@@ -916,7 +916,7 @@
     <string name="search_go" msgid="8298016669822141719">"Căutaţi"</string>
     <string name="searchview_description_search" msgid="6749826639098512120">"Căutaţi"</string>
     <string name="searchview_description_query" msgid="5911778593125355124">"Interogare de căutare"</string>
-    <string name="searchview_description_clear" msgid="1330281990951833033">"Ştergeţi interogarea"</string>
+    <string name="searchview_description_clear" msgid="1330281990951833033">"Ștergeţi interogarea"</string>
     <string name="searchview_description_submit" msgid="2688450133297983542">"Trimiteţi interogarea"</string>
     <string name="searchview_description_voice" msgid="2453203695674994440">"Căutare vocală"</string>
     <string name="enable_explore_by_touch_warning_title" msgid="7460694070309730149">"Activaţi Exploraţi prin atingere?"</string>
@@ -1036,12 +1036,12 @@
     <string name="copy" msgid="2681946229533511987">"Copiaţi"</string>
     <string name="paste" msgid="5629880836805036433">"Inseraţi"</string>
     <string name="replace" msgid="5781686059063148930">"Înlocuiţi..."</string>
-    <string name="delete" msgid="6098684844021697789">"Ştergeţi"</string>
+    <string name="delete" msgid="6098684844021697789">"Ștergeţi"</string>
     <string name="copyUrl" msgid="2538211579596067402">"Copiaţi adresa URL"</string>
     <string name="selectTextMode" msgid="1018691815143165326">"Selectaţi text"</string>
     <string name="textSelectionCABTitle" msgid="5236850394370820357">"Selectare text"</string>
     <string name="addToDictionary" msgid="4352161534510057874">"Adăugaţi în dicţionar"</string>
-    <string name="deleteText" msgid="6979668428458199034">"Ştergeţi"</string>
+    <string name="deleteText" msgid="6979668428458199034">"Ștergeţi"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Metodă de intrare"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Acţiuni pentru text"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Spaţiul de stocare aproape ocupat"</string>
@@ -1056,7 +1056,7 @@
     <string name="capital_off" msgid="6815870386972805832">"DEZACTIVAT"</string>
     <string name="whichApplication" msgid="4533185947064773386">"Finalizare acţiune utilizând"</string>
     <string name="alwaysUse" msgid="4583018368000610438">"Se utilizează în mod prestabilit pentru această acţiune."</string>
-    <string name="clearDefaultHintMsg" msgid="3252584689512077257">"Ştergeţi setările prestabilite din Setări de sistem &gt; Aplicaţii &gt; Descărcate."</string>
+    <string name="clearDefaultHintMsg" msgid="3252584689512077257">"Ștergeţi setările prestabilite din Setări de sistem &gt; Aplicaţii &gt; Descărcate."</string>
     <string name="chooseActivity" msgid="7486876147751803333">"Alegeţi o acţiune"</string>
     <string name="chooseUsbActivity" msgid="6894748416073583509">"Alegeţi o aplicaţie pentru dispozitivul USB"</string>
     <string name="noApplications" msgid="2991814273936504689">"Această acţiune nu poate fi efectuată de nicio aplicaţie."</string>
@@ -1201,7 +1201,7 @@
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Depanarea USB este conectată"</string>
     <string name="adb_active_notification_message" msgid="1016654627626476142">"Atingeţi pentru a dezactiva depanarea USB."</string>
     <string name="select_input_method" msgid="4653387336791222978">"Alegeți metoda de introducere de text"</string>
-    <string name="configure_input_methods" msgid="9091652157722495116">"Configurați metode introducere"</string>
+    <string name="configure_input_methods" msgid="9091652157722495116">"Setați metode introducere text"</string>
     <string name="use_physical_keyboard" msgid="6203112478095117625">"Tastatură fizică"</string>
     <string name="hardware" msgid="7517821086888990278">"Hardware"</string>
     <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Selectaţi aspectul tastaturii"</string>
@@ -1317,7 +1317,7 @@
     <string name="gpsVerifNo" msgid="1146564937346454865">"Nu"</string>
     <string name="sync_too_many_deletes" msgid="5296321850662746890">"Limita pentru ştergere a fost depăşită"</string>
     <string name="sync_too_many_deletes_desc" msgid="496551671008694245">"Există <xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> (de) elemente şterse pentru <xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>, contul <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g>. Ce doriţi să faceţi?"</string>
-    <string name="sync_really_delete" msgid="2572600103122596243">"Ştergeţi elementele"</string>
+    <string name="sync_really_delete" msgid="2572600103122596243">"Ștergeţi elementele"</string>
     <string name="sync_undo_deletes" msgid="2941317360600338602">"Anulaţi aceste ştergeri"</string>
     <string name="sync_do_nothing" msgid="3743764740430821845">"Nu trebuie să luaţi nicio măsură deocamdată"</string>
     <string name="choose_account_label" msgid="5655203089746423927">"Alegeţi un cont"</string>
@@ -1341,7 +1341,7 @@
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Reduceţi valoarea pentru an"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Anulaţi"</string>
-    <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Ştergeţi"</string>
+    <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Ștergeţi"</string>
     <string name="keyboardview_keycode_done" msgid="1992571118466679775">"Terminat"</string>
     <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Schimbarea modului"</string>
     <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 1618110..06e658d 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -34,7 +34,6 @@
 		ProgramCache.cpp \
 		RenderBufferCache.cpp \
 		ResourceCache.cpp \
-		ShapeCache.cpp \
 		SkiaColorFilter.cpp \
 		SkiaShader.cpp \
 		Snapshot.cpp \
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 4642a4f..dc3a4e2 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -224,16 +224,6 @@
             gradientCache.getSize(), gradientCache.getMaxSize());
     log.appendFormat("  PathCache            %8d / %8d\n",
             pathCache.getSize(), pathCache.getMaxSize());
-    log.appendFormat("  CircleShapeCache     %8d / %8d\n",
-            circleShapeCache.getSize(), circleShapeCache.getMaxSize());
-    log.appendFormat("  OvalShapeCache       %8d / %8d\n",
-            ovalShapeCache.getSize(), ovalShapeCache.getMaxSize());
-    log.appendFormat("  RoundRectShapeCache  %8d / %8d\n",
-            roundRectShapeCache.getSize(), roundRectShapeCache.getMaxSize());
-    log.appendFormat("  RectShapeCache       %8d / %8d\n",
-            rectShapeCache.getSize(), rectShapeCache.getMaxSize());
-    log.appendFormat("  ArcShapeCache        %8d / %8d\n",
-            arcShapeCache.getSize(), arcShapeCache.getMaxSize());
     log.appendFormat("  TextDropShadowCache  %8d / %8d\n", dropShadowCache.getSize(),
             dropShadowCache.getMaxSize());
     for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
@@ -253,11 +243,6 @@
     total += gradientCache.getSize();
     total += pathCache.getSize();
     total += dropShadowCache.getSize();
-    total += roundRectShapeCache.getSize();
-    total += circleShapeCache.getSize();
-    total += ovalShapeCache.getSize();
-    total += rectShapeCache.getSize();
-    total += arcShapeCache.getSize();
     for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
         total += fontRenderer->getFontRendererSize(i);
     }
@@ -325,11 +310,6 @@
             fontRenderer->flush();
             textureCache.flush();
             pathCache.clear();
-            roundRectShapeCache.clear();
-            circleShapeCache.clear();
-            ovalShapeCache.clear();
-            rectShapeCache.clear();
-            arcShapeCache.clear();
             // fall through
         case kFlushMode_Layers:
             layerCache.clear();
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index dc32a7e..63836c1 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -36,7 +36,6 @@
 #include "GradientCache.h"
 #include "PatchCache.h"
 #include "ProgramCache.h"
-#include "ShapeCache.h"
 #include "PathCache.h"
 #include "TextDropShadowCache.h"
 #include "FboCache.h"
@@ -269,11 +268,6 @@
     GradientCache gradientCache;
     ProgramCache programCache;
     PathCache pathCache;
-    RoundRectShapeCache roundRectShapeCache;
-    CircleShapeCache circleShapeCache;
-    OvalShapeCache ovalShapeCache;
-    RectShapeCache rectShapeCache;
-    ArcShapeCache arcShapeCache;
     PatchCache patchCache;
     TextDropShadowCache dropShadowCache;
     FboCache fboCache;
diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h
index 773fe82..46beb94 100644
--- a/libs/hwui/Debug.h
+++ b/libs/hwui/Debug.h
@@ -64,7 +64,7 @@
 #define DEBUG_PATCHES_EMPTY_VERTICES 0
 
 // Turn on to display debug info about shapes
-#define DEBUG_SHAPES 0
+#define DEBUG_PATHS 0
 
 // Turn on to display debug info about textures
 #define DEBUG_TEXTURES 0
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index 8455545..5c5bf45 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -17,6 +17,8 @@
 #define LOG_TAG "OpenGLRenderer"
 #define ATRACE_TAG ATRACE_TAG_VIEW
 
+#include <SkCanvas.h>
+
 #include <utils/Trace.h>
 
 #include "Debug.h"
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 4743f58..4944fe8 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <SkCanvas.h>
+
 #include "Debug.h"
 #include "DisplayList.h"
 #include "DisplayListOp.h"
@@ -121,9 +123,7 @@
     }
 
     for (size_t i = 0; i < mPaths.size(); i++) {
-        SkPath* path = mPaths.itemAt(i);
-        caches.pathCache.remove(path);
-        delete path;
+        delete mPaths.itemAt(i);
     }
 
     for (size_t i = 0; i < mMatrices.size(); i++) {
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 11a655e..8b5b54c 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "OpenGLRenderer"
 
 #include <SkCamera.h>
+#include <SkCanvas.h>
 
 #include <private/hwui/DrawGlInfo.h>
 
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 66681e0..e31f6f6 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -2257,9 +2257,11 @@
     // TODO: try clipping large paths to viewport
     PathTessellator::tessellatePath(path, paint, mSnapshot->transform, vertexBuffer);
 
-    SkRect bounds = path.getBounds();
-    PathTessellator::expandBoundsForStroke(bounds, paint, false);
-    dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, currentTransform());
+    if (hasLayer()) {
+        SkRect bounds = path.getBounds();
+        PathTessellator::expandBoundsForStroke(bounds, paint, false);
+        dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, currentTransform());
+    }
 
     return drawVertexBuffer(vertexBuffer, paint);
 }
@@ -2389,7 +2391,7 @@
 
     if (p->getPathEffect() != 0) {
         mCaches.activeTexture(0);
-        const PathTexture* texture = mCaches.roundRectShapeCache.getRoundRect(
+        const PathTexture* texture = mCaches.pathCache.getRoundRect(
                 right - left, bottom - top, rx, ry, p);
         return drawShape(left, top, texture, p);
     }
@@ -2413,7 +2415,7 @@
     }
     if (p->getPathEffect() != 0) {
         mCaches.activeTexture(0);
-        const PathTexture* texture = mCaches.circleShapeCache.getCircle(radius, p);
+        const PathTexture* texture = mCaches.pathCache.getCircle(radius, p);
         return drawShape(x - radius, y - radius, texture, p);
     }
 
@@ -2434,7 +2436,7 @@
 
     if (p->getPathEffect() != 0) {
         mCaches.activeTexture(0);
-        const PathTexture* texture = mCaches.ovalShapeCache.getOval(right - left, bottom - top, p);
+        const PathTexture* texture = mCaches.pathCache.getOval(right - left, bottom - top, p);
         return drawShape(left, top, texture, p);
     }
 
@@ -2460,7 +2462,7 @@
     // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
     if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != 0 || useCenter) {
         mCaches.activeTexture(0);
-        const PathTexture* texture = mCaches.arcShapeCache.getArc(right - left, bottom - top,
+        const PathTexture* texture = mCaches.pathCache.getArc(right - left, bottom - top,
                 startAngle, sweepAngle, useCenter, p);
         return drawShape(left, top, texture, p);
     }
@@ -2495,7 +2497,7 @@
                 p->getStrokeMiter() != SkPaintDefaults_MiterLimit) {
             mCaches.activeTexture(0);
             const PathTexture* texture =
-                    mCaches.rectShapeCache.getRect(right - left, bottom - top, p);
+                    mCaches.pathCache.getRect(right - left, bottom - top, p);
             return drawShape(left, top, texture, p);
         }
 
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index fb687cd..490c22a0 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2013 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.
@@ -15,19 +15,301 @@
  */
 
 #define LOG_TAG "OpenGLRenderer"
+#define ATRACE_TAG ATRACE_TAG_VIEW
 
-#include <utils/Mutex.h>
+#include <SkBitmap.h>
+#include <SkCanvas.h>
+#include <SkPaint.h>
+#include <SkPath.h>
+#include <SkRect.h>
 
-#include <sys/sysinfo.h>
+#include <utils/JenkinsHash.h>
+#include <utils/Trace.h>
 
 #include "Caches.h"
 #include "PathCache.h"
-#include "Properties.h"
+
+#include "thread/Signal.h"
+#include "thread/Task.h"
+#include "thread/TaskProcessor.h"
 
 namespace android {
 namespace uirenderer {
 
 ///////////////////////////////////////////////////////////////////////////////
+// Cache entries
+///////////////////////////////////////////////////////////////////////////////
+
+PathDescription::PathDescription():
+        type(kShapeNone),
+        join(SkPaint::kDefault_Join),
+        cap(SkPaint::kDefault_Cap),
+        style(SkPaint::kFill_Style),
+        miter(4.0f),
+        strokeWidth(1.0f),
+        pathEffect(NULL) {
+    memset(&shape, 0, sizeof(Shape));
+}
+
+PathDescription::PathDescription(ShapeType type, SkPaint* paint):
+        type(type),
+        join(paint->getStrokeJoin()),
+        cap(paint->getStrokeCap()),
+        style(paint->getStyle()),
+        miter(paint->getStrokeMiter()),
+        strokeWidth(paint->getStrokeWidth()),
+        pathEffect(paint->getPathEffect()) {
+    memset(&shape, 0, sizeof(Shape));
+}
+
+hash_t PathDescription::hash() const {
+    uint32_t hash = JenkinsHashMix(0, type);
+    hash = JenkinsHashMix(hash, join);
+    hash = JenkinsHashMix(hash, cap);
+    hash = JenkinsHashMix(hash, style);
+    hash = JenkinsHashMix(hash, android::hash_type(miter));
+    hash = JenkinsHashMix(hash, android::hash_type(strokeWidth));
+    hash = JenkinsHashMix(hash, android::hash_type(pathEffect));
+    hash = JenkinsHashMixBytes(hash, (uint8_t*) &shape, sizeof(Shape));
+    return JenkinsHashWhiten(hash);
+}
+
+int PathDescription::compare(const PathDescription& rhs) const {
+    return memcmp(this, &rhs, sizeof(PathDescription));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Utilities
+///////////////////////////////////////////////////////////////////////////////
+
+bool PathCache::canDrawAsConvexPath(SkPath* path, SkPaint* paint) {
+    // NOTE: This should only be used after PathTessellator handles joins properly
+    return paint->getPathEffect() == NULL && path->getConvexity() == SkPath::kConvex_Convexity;
+}
+
+void PathCache::computePathBounds(const SkPath* path, const SkPaint* paint,
+        float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
+    const SkRect& bounds = path->getBounds();
+    PathCache::computeBounds(bounds, paint, left, top, offset, width, height);
+}
+
+void PathCache::computeBounds(const SkRect& bounds, const SkPaint* paint,
+        float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
+    const float pathWidth = fmax(bounds.width(), 1.0f);
+    const float pathHeight = fmax(bounds.height(), 1.0f);
+
+    left = bounds.fLeft;
+    top = bounds.fTop;
+
+    offset = (int) floorf(fmax(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f);
+
+    width = uint32_t(pathWidth + offset * 2.0 + 0.5);
+    height = uint32_t(pathHeight + offset * 2.0 + 0.5);
+}
+
+static void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) {
+    bitmap.setConfig(SkBitmap::kA8_Config, width, height);
+    bitmap.allocPixels();
+    bitmap.eraseColor(0);
+}
+
+static void initPaint(SkPaint& paint) {
+    // Make sure the paint is opaque, color, alpha, filter, etc.
+    // will be applied later when compositing the alpha8 texture
+    paint.setColor(0xff000000);
+    paint.setAlpha(255);
+    paint.setColorFilter(NULL);
+    paint.setMaskFilter(NULL);
+    paint.setShader(NULL);
+    SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode);
+    SkSafeUnref(paint.setXfermode(mode));
+}
+
+static void drawPath(const SkPath *path, const SkPaint* paint, SkBitmap& bitmap,
+        float left, float top, float offset, uint32_t width, uint32_t height) {
+    initBitmap(bitmap, width, height);
+
+    SkPaint pathPaint(*paint);
+    initPaint(pathPaint);
+
+    SkCanvas canvas(bitmap);
+    canvas.translate(-left + offset, -top + offset);
+    canvas.drawPath(*path, pathPaint);
+}
+
+static PathTexture* createTexture(float left, float top, float offset,
+        uint32_t width, uint32_t height, uint32_t id) {
+    PathTexture* texture = new PathTexture();
+    texture->left = left;
+    texture->top = top;
+    texture->offset = offset;
+    texture->width = width;
+    texture->height = height;
+    texture->generation = id;
+    return texture;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Cache constructor/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+PathCache::PathCache():
+        mCache(LruCache<PathDescription, PathTexture*>::kUnlimitedCapacity),
+        mSize(0), mMaxSize(MB(DEFAULT_PATH_CACHE_SIZE)) {
+    char property[PROPERTY_VALUE_MAX];
+    if (property_get(PROPERTY_PATH_CACHE_SIZE, property, NULL) > 0) {
+        INIT_LOGD("  Setting %s cache size to %sMB", name, property);
+        setMaxSize(MB(atof(property)));
+    } else {
+        INIT_LOGD("  Using default %s cache size of %.2fMB", name, DEFAULT_PATH_CACHE_SIZE);
+    }
+    init();
+}
+
+PathCache::~PathCache() {
+    mCache.clear();
+}
+
+void PathCache::init() {
+    mCache.setOnEntryRemovedListener(this);
+
+    GLint maxTextureSize;
+    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
+    mMaxTextureSize = maxTextureSize;
+
+    mDebugEnabled = readDebugLevel() & kDebugCaches;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Size management
+///////////////////////////////////////////////////////////////////////////////
+
+uint32_t PathCache::getSize() {
+    return mSize;
+}
+
+uint32_t PathCache::getMaxSize() {
+    return mMaxSize;
+}
+
+void PathCache::setMaxSize(uint32_t maxSize) {
+    mMaxSize = maxSize;
+    while (mSize > mMaxSize) {
+        mCache.removeOldest();
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Callbacks
+///////////////////////////////////////////////////////////////////////////////
+
+void PathCache::operator()(PathDescription& entry, PathTexture*& texture) {
+    removeTexture(texture);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Caching
+///////////////////////////////////////////////////////////////////////////////
+
+void PathCache::removeTexture(PathTexture* texture) {
+    if (texture) {
+        const uint32_t size = texture->width * texture->height;
+        mSize -= size;
+
+        PATH_LOGD("PathCache::delete name, size, mSize = %d, %d, %d",
+                texture->id, size, mSize);
+        if (mDebugEnabled) {
+            ALOGD("Shape deleted, size = %d", size);
+        }
+
+        if (texture->id) {
+            glDeleteTextures(1, &texture->id);
+        }
+        delete texture;
+    }
+}
+
+void PathCache::purgeCache(uint32_t width, uint32_t height) {
+    const uint32_t size = width * height;
+    // Don't even try to cache a bitmap that's bigger than the cache
+    if (size < mMaxSize) {
+        while (mSize + size > mMaxSize) {
+            mCache.removeOldest();
+        }
+    }
+}
+
+void PathCache::trim() {
+    while (mSize > mMaxSize) {
+        mCache.removeOldest();
+    }
+}
+
+PathTexture* PathCache::addTexture(const PathDescription& entry, const SkPath *path,
+        const SkPaint* paint) {
+    ATRACE_CALL();
+
+    float left, top, offset;
+    uint32_t width, height;
+    computePathBounds(path, paint, left, top, offset, width, height);
+
+    if (!checkTextureSize(width, height)) return NULL;
+
+    purgeCache(width, height);
+
+    SkBitmap bitmap;
+    drawPath(path, paint, bitmap, left, top, offset, width, height);
+
+    PathTexture* texture = createTexture(left, top, offset, width, height,
+            path->getGenerationID());
+    addTexture(entry, &bitmap, texture);
+
+    return texture;
+}
+
+void PathCache::addTexture(const PathDescription& entry, SkBitmap* bitmap, PathTexture* texture) {
+    generateTexture(*bitmap, texture);
+
+    uint32_t size = texture->width * texture->height;
+    if (size < mMaxSize) {
+        mSize += size;
+        PATH_LOGD("PathCache::get/create: name, size, mSize = %d, %d, %d",
+                texture->id, size, mSize);
+        if (mDebugEnabled) {
+            ALOGD("Shape created, size = %d", size);
+        }
+        mCache.put(entry, texture);
+    } else {
+        texture->cleanup = true;
+    }
+}
+
+void PathCache::clear() {
+    mCache.clear();
+}
+
+void PathCache::generateTexture(SkBitmap& bitmap, Texture* texture) {
+    SkAutoLockPixels alp(bitmap);
+    if (!bitmap.readyToDraw()) {
+        ALOGE("Cannot generate texture from bitmap");
+        return;
+    }
+
+    glGenTextures(1, &texture->id);
+
+    glBindTexture(GL_TEXTURE_2D, texture->id);
+    // Textures are Alpha8
+    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+    texture->blend = true;
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0,
+            GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.getPixels());
+
+    texture->setFilter(GL_LINEAR);
+    texture->setWrap(GL_CLAMP_TO_EDGE);
+}
+
+///////////////////////////////////////////////////////////////////////////////
 // Path precaching
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -52,7 +334,7 @@
 
     if (width <= mMaxTextureSize && height <= mMaxTextureSize) {
         SkBitmap* bitmap = new SkBitmap();
-        PathCache::drawPath(t->path, t->paint, *bitmap, left, top, offset, width, height);
+        drawPath(t->path, t->paint, *bitmap, left, top, offset, width, height);
         t->setResult(bitmap);
     } else {
         texture->width = 0;
@@ -62,23 +344,17 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-// Path cache
+// Paths
 ///////////////////////////////////////////////////////////////////////////////
 
-PathCache::PathCache(): ShapeCache<PathCacheEntry>("path",
-        PROPERTY_PATH_CACHE_SIZE, DEFAULT_PATH_CACHE_SIZE) {
-}
-
-PathCache::~PathCache() {
-}
-
 void PathCache::remove(SkPath* path) {
-    Vector<PathCacheEntry> pathsToRemove;
-    LruCache<PathCacheEntry, PathTexture*>::Iterator i(mCache);
+    Vector<PathDescription> pathsToRemove;
+    LruCache<PathDescription, PathTexture*>::Iterator i(mCache);
 
     while (i.next()) {
-        const PathCacheEntry& key = i.key();
-        if (key.path == path || key.path == path->getSourcePath()) {
+        const PathDescription& key = i.key();
+        if (key.type == kShapePath &&
+                (key.shape.path.mPath == path || key.shape.path.mPath == path->getSourcePath())) {
             pathsToRemove.push(key);
         }
     }
@@ -121,7 +397,9 @@
 PathTexture* PathCache::get(SkPath* path, SkPaint* paint) {
     path = getSourcePath(path);
 
-    PathCacheEntry entry(path, paint);
+    PathDescription entry(kShapePath, paint);
+    entry.shape.path.mPath = path;
+
     PathTexture* texture = mCache.get(entry);
 
     if (!texture) {
@@ -159,7 +437,9 @@
 
     path = getSourcePath(path);
 
-    PathCacheEntry entry(path, paint);
+    PathDescription entry(kShapePath, paint);
+    entry.shape.path.mPath = path;
+
     PathTexture* texture = mCache.get(entry);
 
     bool generate = false;
@@ -193,5 +473,130 @@
     }
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// Rounded rects
+///////////////////////////////////////////////////////////////////////////////
+
+PathTexture* PathCache::getRoundRect(float width, float height,
+        float rx, float ry, SkPaint* paint) {
+    PathDescription entry(kShapeRoundRect, paint);
+    entry.shape.roundRect.mWidth = width;
+    entry.shape.roundRect.mHeight = height;
+    entry.shape.roundRect.mRx = rx;
+    entry.shape.roundRect.mRy = ry;
+
+    PathTexture* texture = get(entry);
+
+    if (!texture) {
+        SkPath path;
+        SkRect r;
+        r.set(0.0f, 0.0f, width, height);
+        path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
+
+        texture = addTexture(entry, &path, paint);
+    }
+
+    return texture;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Circles
+///////////////////////////////////////////////////////////////////////////////
+
+PathTexture* PathCache::getCircle(float radius, SkPaint* paint) {
+    PathDescription entry(kShapeCircle, paint);
+    entry.shape.circle.mRadius = radius;
+
+    PathTexture* texture = get(entry);
+
+    if (!texture) {
+        SkPath path;
+        path.addCircle(radius, radius, radius, SkPath::kCW_Direction);
+
+        texture = addTexture(entry, &path, paint);
+    }
+
+    return texture;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Ovals
+///////////////////////////////////////////////////////////////////////////////
+
+PathTexture* PathCache::getOval(float width, float height, SkPaint* paint) {
+    PathDescription entry(kShapeOval, paint);
+    entry.shape.oval.mWidth = width;
+    entry.shape.oval.mHeight = height;
+
+    PathTexture* texture = get(entry);
+
+    if (!texture) {
+        SkPath path;
+        SkRect r;
+        r.set(0.0f, 0.0f, width, height);
+        path.addOval(r, SkPath::kCW_Direction);
+
+        texture = addTexture(entry, &path, paint);
+    }
+
+    return texture;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Rects
+///////////////////////////////////////////////////////////////////////////////
+
+PathTexture* PathCache::getRect(float width, float height, SkPaint* paint) {
+    PathDescription entry(kShapeRect, paint);
+    entry.shape.rect.mWidth = width;
+    entry.shape.rect.mHeight = height;
+
+    PathTexture* texture = get(entry);
+
+    if (!texture) {
+        SkPath path;
+        SkRect r;
+        r.set(0.0f, 0.0f, width, height);
+        path.addRect(r, SkPath::kCW_Direction);
+
+        texture = addTexture(entry, &path, paint);
+    }
+
+    return texture;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Arcs
+///////////////////////////////////////////////////////////////////////////////
+
+PathTexture* PathCache::getArc(float width, float height,
+        float startAngle, float sweepAngle, bool useCenter, SkPaint* paint) {
+    PathDescription entry(kShapeArc, paint);
+    entry.shape.arc.mWidth = width;
+    entry.shape.arc.mHeight = height;
+    entry.shape.arc.mStartAngle = startAngle;
+    entry.shape.arc.mSweepAngle = sweepAngle;
+    entry.shape.arc.mUseCenter = useCenter;
+
+    PathTexture* texture = get(entry);
+
+    if (!texture) {
+        SkPath path;
+        SkRect r;
+        r.set(0.0f, 0.0f, width, height);
+        if (useCenter) {
+            path.moveTo(r.centerX(), r.centerY());
+        }
+        path.arcTo(r, startAngle, sweepAngle, !useCenter);
+        if (useCenter) {
+            path.close();
+        }
+
+        texture = addTexture(entry, &path, paint);
+    }
+
+    return texture;
+}
+
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index 27031a5..e6d92df 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2013 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.
@@ -17,17 +17,21 @@
 #ifndef ANDROID_HWUI_PATH_CACHE_H
 #define ANDROID_HWUI_PATH_CACHE_H
 
-#include <utils/Thread.h>
+#include <GLES2/gl2.h>
+
+#include <utils/LruCache.h>
+#include <utils/Mutex.h>
 #include <utils/Vector.h>
 
 #include "Debug.h"
-#include "ShapeCache.h"
-#include "thread/Signal.h"
-#include "thread/Task.h"
-#include "thread/TaskProcessor.h"
+#include "Properties.h"
+#include "Texture.h"
 
+class SkBitmap;
+class SkCanvas;
 class SkPaint;
 class SkPath;
+class SkRect;
 
 namespace android {
 namespace uirenderer {
@@ -35,56 +39,181 @@
 class Caches;
 
 ///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Debug
+#if DEBUG_PATHS
+    #define PATH_LOGD(...) ALOGD(__VA_ARGS__)
+#else
+    #define PATH_LOGD(...)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
 // Classes
 ///////////////////////////////////////////////////////////////////////////////
 
-struct PathCacheEntry: public ShapeCacheEntry {
-    PathCacheEntry(SkPath* path, SkPaint* paint):
-            ShapeCacheEntry(ShapeCacheEntry::kShapePath, paint) {
-        this->path = path;
+/**
+ * Alpha texture used to represent a path.
+ */
+struct PathTexture: public Texture {
+    PathTexture(): Texture() {
     }
 
-    PathCacheEntry(): ShapeCacheEntry() {
-        path = NULL;
+    ~PathTexture() {
+        clearTask();
     }
 
-    hash_t hash() const {
-        uint32_t hash = ShapeCacheEntry::hash();
-        hash = JenkinsHashMix(hash, android::hash_type(path));
-        return JenkinsHashWhiten(hash);
+    /**
+     * Left coordinate of the path bounds.
+     */
+    float left;
+    /**
+     * Top coordinate of the path bounds.
+     */
+    float top;
+    /**
+     * Offset to draw the path at the correct origin.
+     */
+    float offset;
+
+    sp<Task<SkBitmap*> > task() const {
+        return mTask;
     }
 
-    int compare(const ShapeCacheEntry& r) const {
-        int deltaInt = ShapeCacheEntry::compare(r);
-        if (deltaInt != 0) return deltaInt;
-
-        const PathCacheEntry& rhs = (const PathCacheEntry&) r;
-        return path - rhs.path;
+    void setTask(const sp<Task<SkBitmap*> >& task) {
+        mTask = task;
     }
 
-    SkPath* path;
+    void clearTask() {
+        if (mTask != NULL) {
+            mTask.clear();
+        }
+    }
 
-}; // PathCacheEntry
+private:
+    sp<Task<SkBitmap*> > mTask;
+}; // struct PathTexture
 
-inline hash_t hash_type(const PathCacheEntry& entry) {
-    return entry.hash();
-}
+enum ShapeType {
+    kShapeNone,
+    kShapeRect,
+    kShapeRoundRect,
+    kShapeCircle,
+    kShapeOval,
+    kShapeArc,
+    kShapePath
+};
+
+struct PathDescription {
+    ShapeType type;
+    SkPaint::Join join;
+    SkPaint::Cap cap;
+    SkPaint::Style style;
+    float miter;
+    float strokeWidth;
+    SkPathEffect* pathEffect;
+    union Shape {
+        struct Path {
+            SkPath* mPath;
+        } path;
+        struct RoundRect {
+            float mWidth;
+            float mHeight;
+            float mRx;
+            float mRy;
+        } roundRect;
+        struct Circle {
+            float mRadius;
+        } circle;
+        struct Oval {
+            float mWidth;
+            float mHeight;
+        } oval;
+        struct Rect {
+            float mWidth;
+            float mHeight;
+        } rect;
+        struct Arc {
+            float mWidth;
+            float mHeight;
+            float mStartAngle;
+            float mSweepAngle;
+            bool mUseCenter;
+        } arc;
+    } shape;
+
+    PathDescription();
+    PathDescription(ShapeType shapeType, SkPaint* paint);
+
+    hash_t hash() const;
+
+    int compare(const PathDescription& rhs) const;
+
+    bool operator==(const PathDescription& other) const {
+        return compare(other) == 0;
+    }
+
+    bool operator!=(const PathDescription& other) const {
+        return compare(other) != 0;
+    }
+
+    friend inline int strictly_order_type(
+            const PathDescription& lhs, const PathDescription& rhs) {
+        return lhs.compare(rhs) < 0;
+    }
+
+    friend inline int compare_type(const PathDescription& lhs, const PathDescription& rhs) {
+        return lhs.compare(rhs);
+    }
+
+    friend inline hash_t hash_type(const PathDescription& entry) {
+        return entry.hash();
+    }
+};
 
 /**
- * A simple LRU path cache. The cache has a maximum size expressed in bytes.
+ * A simple LRU shape cache. The cache has a maximum size expressed in bytes.
  * Any texture added to the cache causing the cache to grow beyond the maximum
  * allowed size will also cause the oldest texture to be kicked out.
  */
-class PathCache: public ShapeCache<PathCacheEntry> {
+class PathCache: public OnEntryRemoved<PathDescription, PathTexture*> {
 public:
     PathCache();
     ~PathCache();
 
     /**
-     * Returns the texture associated with the specified path. If the texture
-     * cannot be found in the cache, a new texture is generated.
+     * Used as a callback when an entry is removed from the cache.
+     * Do not invoke directly.
      */
+    void operator()(PathDescription& path, PathTexture*& texture);
+
+    /**
+     * Clears the cache. This causes all textures to be deleted.
+     */
+    void clear();
+
+    /**
+     * Sets the maximum size of the cache in bytes.
+     */
+    void setMaxSize(uint32_t maxSize);
+    /**
+     * Returns the maximum size of the cache in bytes.
+     */
+    uint32_t getMaxSize();
+    /**
+     * Returns the current size of the cache in bytes.
+     */
+    uint32_t getSize();
+
+    PathTexture* getRoundRect(float width, float height, float rx, float ry, SkPaint* paint);
+    PathTexture* getCircle(float radius, SkPaint* paint);
+    PathTexture* getOval(float width, float height, SkPaint* paint);
+    PathTexture* getRect(float width, float height, SkPaint* paint);
+    PathTexture* getArc(float width, float height, float startAngle, float sweepAngle,
+            bool useCenter, SkPaint* paint);
     PathTexture* get(SkPath* path, SkPaint* paint);
+
     /**
      * Removes an entry.
      */
@@ -98,10 +227,63 @@
      * Process deferred removals.
      */
     void clearGarbage();
+    /**
+     * Trims the contents of the cache, removing items until it's under its
+     * specified limit.
+     *
+     * Trimming is used for caches that support pre-caching from a worker
+     * thread. During pre-caching the maximum limit of the cache can be
+     * exceeded for the duration of the frame. It is therefore required to
+     * trim the cache at the end of the frame to keep the total amount of
+     * memory used under control.
+     */
+    void trim();
 
+    /**
+     * Precaches the specified path using background threads.
+     */
     void precache(SkPath* path, SkPaint* paint);
 
+    static bool canDrawAsConvexPath(SkPath* path, SkPaint* paint);
+    static void computePathBounds(const SkPath* path, const SkPaint* paint,
+            float& left, float& top, float& offset, uint32_t& width, uint32_t& height);
+    static void computeBounds(const SkRect& bounds, const SkPaint* paint,
+            float& left, float& top, float& offset, uint32_t& width, uint32_t& height);
+
 private:
+    PathTexture* addTexture(const PathDescription& entry,
+            const SkPath *path, const SkPaint* paint);
+    PathTexture* addTexture(const PathDescription& entry, SkBitmap* bitmap);
+    void addTexture(const PathDescription& entry, SkBitmap* bitmap, PathTexture* texture);
+
+    PathTexture* get(const PathDescription& entry) {
+        return mCache.get(entry);
+    }
+
+    /**
+     * Ensures there is enough space in the cache for a texture of the specified
+     * dimensions.
+     */
+    void purgeCache(uint32_t width, uint32_t height);
+
+    void removeTexture(PathTexture* texture);
+
+    bool checkTextureSize(uint32_t width, uint32_t height) {
+        if (width > mMaxTextureSize || height > mMaxTextureSize) {
+            ALOGW("Shape too large to be rendered into a texture (%dx%d, max=%dx%d)",
+                    width, height, mMaxTextureSize, mMaxTextureSize);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Generates the texture from a bitmap into the specified texture structure.
+     */
+    void generateTexture(SkBitmap& bitmap, Texture* texture);
+
+    void init();
+
     class PathTask: public Task<SkBitmap*> {
     public:
         PathTask(SkPath* path, SkPaint* paint, PathTexture* texture):
@@ -128,6 +310,13 @@
         uint32_t mMaxTextureSize;
     };
 
+    LruCache<PathDescription, PathTexture*> mCache;
+    uint32_t mSize;
+    uint32_t mMaxSize;
+    GLuint mMaxTextureSize;
+
+    bool mDebugEnabled;
+
     sp<PathProcessor> mProcessor;
     Vector<SkPath*> mGarbage;
     mutable Mutex mLock;
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index b8559bc8..5f39abf 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -115,7 +115,6 @@
 #define PROPERTY_RENDER_BUFFER_CACHE_SIZE "ro.hwui.r_buffer_cache_size"
 #define PROPERTY_GRADIENT_CACHE_SIZE "ro.hwui.gradient_cache_size"
 #define PROPERTY_PATH_CACHE_SIZE "ro.hwui.path_cache_size"
-#define PROPERTY_SHAPE_CACHE_SIZE "ro.hwui.shape_cache_size"
 #define PROPERTY_DROP_SHADOW_CACHE_SIZE "ro.hwui.drop_shadow_cache_size"
 #define PROPERTY_FBO_CACHE_SIZE "ro.hwui.fbo_cache_size"
 
@@ -159,8 +158,7 @@
 #define DEFAULT_TEXTURE_CACHE_SIZE 24.0f
 #define DEFAULT_LAYER_CACHE_SIZE 16.0f
 #define DEFAULT_RENDER_BUFFER_CACHE_SIZE 2.0f
-#define DEFAULT_PATH_CACHE_SIZE 4.0f
-#define DEFAULT_SHAPE_CACHE_SIZE 1.0f
+#define DEFAULT_PATH_CACHE_SIZE 10.0f
 #define DEFAULT_PATCH_CACHE_SIZE 512
 #define DEFAULT_GRADIENT_CACHE_SIZE 0.5f
 #define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f
diff --git a/libs/hwui/ShapeCache.cpp b/libs/hwui/ShapeCache.cpp
deleted file mode 100644
index 5a23235..0000000
--- a/libs/hwui/ShapeCache.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-#define LOG_TAG "OpenGLRenderer"
-
-#include "ShapeCache.h"
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Rounded rects
-///////////////////////////////////////////////////////////////////////////////
-
-RoundRectShapeCache::RoundRectShapeCache(): ShapeCache<RoundRectShapeCacheEntry>(
-        "round rect", PROPERTY_SHAPE_CACHE_SIZE, DEFAULT_SHAPE_CACHE_SIZE) {
-}
-
-PathTexture* RoundRectShapeCache::getRoundRect(float width, float height,
-        float rx, float ry, SkPaint* paint) {
-    RoundRectShapeCacheEntry entry(width, height, rx, ry, paint);
-    PathTexture* texture = get(entry);
-
-    if (!texture) {
-        SkPath path;
-        SkRect r;
-        r.set(0.0f, 0.0f, width, height);
-        path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
-
-        texture = addTexture(entry, &path, paint);
-    }
-
-    return texture;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Circles
-///////////////////////////////////////////////////////////////////////////////
-
-CircleShapeCache::CircleShapeCache(): ShapeCache<CircleShapeCacheEntry>(
-        "circle", PROPERTY_SHAPE_CACHE_SIZE, DEFAULT_SHAPE_CACHE_SIZE) {
-}
-
-PathTexture* CircleShapeCache::getCircle(float radius, SkPaint* paint) {
-    CircleShapeCacheEntry entry(radius, paint);
-    PathTexture* texture = get(entry);
-
-    if (!texture) {
-        SkPath path;
-        path.addCircle(radius, radius, radius, SkPath::kCW_Direction);
-
-        texture = addTexture(entry, &path, paint);
-    }
-
-    return texture;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Ovals
-///////////////////////////////////////////////////////////////////////////////
-
-OvalShapeCache::OvalShapeCache(): ShapeCache<OvalShapeCacheEntry>(
-        "oval", PROPERTY_SHAPE_CACHE_SIZE, DEFAULT_SHAPE_CACHE_SIZE) {
-}
-
-PathTexture* OvalShapeCache::getOval(float width, float height, SkPaint* paint) {
-    OvalShapeCacheEntry entry(width, height, paint);
-    PathTexture* texture = get(entry);
-
-    if (!texture) {
-        SkPath path;
-        SkRect r;
-        r.set(0.0f, 0.0f, width, height);
-        path.addOval(r, SkPath::kCW_Direction);
-
-        texture = addTexture(entry, &path, paint);
-    }
-
-    return texture;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Rects
-///////////////////////////////////////////////////////////////////////////////
-
-RectShapeCache::RectShapeCache(): ShapeCache<RectShapeCacheEntry>(
-        "rect", PROPERTY_SHAPE_CACHE_SIZE, DEFAULT_SHAPE_CACHE_SIZE) {
-}
-
-PathTexture* RectShapeCache::getRect(float width, float height, SkPaint* paint) {
-    RectShapeCacheEntry entry(width, height, paint);
-    PathTexture* texture = get(entry);
-
-    if (!texture) {
-        SkRect bounds;
-        bounds.set(0.0f, 0.0f, width, height);
-
-        float left, top, offset;
-        uint32_t rectWidth, rectHeight;
-        computeBounds(bounds, paint, left, top, offset, rectWidth, rectHeight);
-
-        if (!checkTextureSize(rectWidth, rectHeight)) return NULL;
-
-        purgeCache(rectWidth, rectHeight);
-
-        SkBitmap bitmap;
-        initBitmap(bitmap, rectWidth, rectHeight);
-
-        SkPaint pathPaint(*paint);
-        initPaint(pathPaint);
-
-        SkCanvas canvas(bitmap);
-        canvas.translate(-left + offset, -top + offset);
-        canvas.drawRect(bounds, pathPaint);
-
-        texture = createTexture(0, 0, offset, rectWidth, rectHeight, 0);
-        addTexture(entry, &bitmap, texture);
-    }
-
-    return texture;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Arcs
-///////////////////////////////////////////////////////////////////////////////
-
-ArcShapeCache::ArcShapeCache(): ShapeCache<ArcShapeCacheEntry>(
-        "arc", PROPERTY_SHAPE_CACHE_SIZE, DEFAULT_SHAPE_CACHE_SIZE) {
-}
-
-PathTexture* ArcShapeCache::getArc(float width, float height,
-        float startAngle, float sweepAngle, bool useCenter, SkPaint* paint) {
-    ArcShapeCacheEntry entry(width, height, startAngle, sweepAngle, useCenter, paint);
-    PathTexture* texture = get(entry);
-
-    if (!texture) {
-        SkPath path;
-        SkRect r;
-        r.set(0.0f, 0.0f, width, height);
-        if (useCenter) {
-            path.moveTo(r.centerX(), r.centerY());
-        }
-        path.arcTo(r, startAngle, sweepAngle, !useCenter);
-        if (useCenter) {
-            path.close();
-        }
-
-        texture = addTexture(entry, &path, paint);
-    }
-
-    return texture;
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/ShapeCache.h b/libs/hwui/ShapeCache.h
deleted file mode 100644
index 92314b0..0000000
--- a/libs/hwui/ShapeCache.h
+++ /dev/null
@@ -1,816 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-#ifndef ANDROID_HWUI_SHAPE_CACHE_H
-#define ANDROID_HWUI_SHAPE_CACHE_H
-
-#define ATRACE_TAG ATRACE_TAG_VIEW
-
-#include <GLES2/gl2.h>
-
-#include <SkBitmap.h>
-#include <SkCanvas.h>
-#include <SkPaint.h>
-#include <SkPath.h>
-#include <SkRect.h>
-
-#include <utils/JenkinsHash.h>
-#include <utils/LruCache.h>
-#include <utils/Trace.h>
-
-#include "Debug.h"
-#include "Properties.h"
-#include "Texture.h"
-#include "thread/Task.h"
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-// Debug
-#if DEBUG_SHAPES
-    #define SHAPE_LOGD(...) ALOGD(__VA_ARGS__)
-#else
-    #define SHAPE_LOGD(...)
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-// Classes
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * Alpha texture used to represent a path.
- */
-struct PathTexture: public Texture {
-    PathTexture(): Texture() {
-    }
-
-    ~PathTexture() {
-        clearTask();
-    }
-
-    /**
-     * Left coordinate of the path bounds.
-     */
-    float left;
-    /**
-     * Top coordinate of the path bounds.
-     */
-    float top;
-    /**
-     * Offset to draw the path at the correct origin.
-     */
-    float offset;
-
-    sp<Task<SkBitmap*> > task() const {
-        return mTask;
-    }
-
-    void setTask(const sp<Task<SkBitmap*> >& task) {
-        mTask = task;
-    }
-
-    void clearTask() {
-        if (mTask != NULL) {
-            mTask.clear();
-        }
-    }
-
-private:
-    sp<Task<SkBitmap*> > mTask;
-}; // struct PathTexture
-
-/**
- * Describe a shape in the shape cache.
- */
-struct ShapeCacheEntry {
-    enum ShapeType {
-        kShapeNone,
-        kShapeRect,
-        kShapeRoundRect,
-        kShapeCircle,
-        kShapeOval,
-        kShapeArc,
-        kShapePath
-    };
-
-    ShapeCacheEntry() {
-        shapeType = kShapeNone;
-        join = SkPaint::kDefault_Join;
-        cap = SkPaint::kDefault_Cap;
-        style = SkPaint::kFill_Style;
-        miter = 4.0f;
-        strokeWidth = 1.0f;
-        pathEffect = NULL;
-    }
-
-    ShapeCacheEntry(ShapeType type, SkPaint* paint) {
-        shapeType = type;
-        join = paint->getStrokeJoin();
-        cap = paint->getStrokeCap();
-        miter = paint->getStrokeMiter();
-        strokeWidth = paint->getStrokeWidth();
-        style = paint->getStyle();
-        pathEffect = paint->getPathEffect();
-    }
-
-    virtual ~ShapeCacheEntry() {
-    }
-
-    virtual hash_t hash() const {
-        uint32_t hash = JenkinsHashMix(0, shapeType);
-        hash = JenkinsHashMix(hash, join);
-        hash = JenkinsHashMix(hash, cap);
-        hash = JenkinsHashMix(hash, style);
-        hash = JenkinsHashMix(hash, android::hash_type(miter));
-        hash = JenkinsHashMix(hash, android::hash_type(strokeWidth));
-        hash = JenkinsHashMix(hash, android::hash_type(pathEffect));
-        return JenkinsHashWhiten(hash);
-    }
-
-    virtual int compare(const ShapeCacheEntry& rhs) const {
-        int deltaInt = shapeType - rhs.shapeType;
-        if (deltaInt != 0) return deltaInt;
-
-        deltaInt = join - rhs.join;
-        if (deltaInt != 0) return deltaInt;
-
-        deltaInt = cap - rhs.cap;
-        if (deltaInt != 0) return deltaInt;
-
-        deltaInt = style - rhs.style;
-        if (deltaInt != 0) return deltaInt;
-
-        if (miter < rhs.miter) return -1;
-        if (miter > rhs.miter) return +1;
-
-        if (strokeWidth < rhs.strokeWidth) return -1;
-        if (strokeWidth > rhs.strokeWidth) return +1;
-
-        if (pathEffect < rhs.pathEffect) return -1;
-        if (pathEffect > rhs.pathEffect) return +1;
-
-        return 0;
-    }
-
-    bool operator==(const ShapeCacheEntry& other) const {
-        return compare(other) == 0;
-    }
-
-    bool operator!=(const ShapeCacheEntry& other) const {
-        return compare(other) != 0;
-    }
-
-    ShapeType shapeType;
-    SkPaint::Join join;
-    SkPaint::Cap cap;
-    SkPaint::Style style;
-    float miter;
-    float strokeWidth;
-    SkPathEffect* pathEffect;
-}; // struct ShapeCacheEntry
-
-// Cache support
-
-inline int strictly_order_type(const ShapeCacheEntry& lhs, const ShapeCacheEntry& rhs) {
-    return lhs.compare(rhs) < 0;
-}
-
-inline int compare_type(const ShapeCacheEntry& lhs, const ShapeCacheEntry& rhs) {
-    return lhs.compare(rhs);
-}
-
-inline hash_t hash_type(const ShapeCacheEntry& entry) {
-    return entry.hash();
-}
-
-struct RoundRectShapeCacheEntry: public ShapeCacheEntry {
-    RoundRectShapeCacheEntry(float width, float height, float rx, float ry, SkPaint* paint):
-            ShapeCacheEntry(ShapeCacheEntry::kShapeRoundRect, paint) {
-        mWidth = width;
-        mHeight = height;
-        mRx = rx;
-        mRy = ry;
-    }
-
-    RoundRectShapeCacheEntry(): ShapeCacheEntry() {
-        mWidth = 0;
-        mHeight = 0;
-        mRx = 0;
-        mRy = 0;
-    }
-
-    hash_t hash() const {
-        uint32_t hash = ShapeCacheEntry::hash();
-        hash = JenkinsHashMix(hash, android::hash_type(mWidth));
-        hash = JenkinsHashMix(hash, android::hash_type(mHeight));
-        hash = JenkinsHashMix(hash, android::hash_type(mRx));
-        hash = JenkinsHashMix(hash, android::hash_type(mRy));
-        return JenkinsHashWhiten(hash);
-    }
-
-    int compare(const ShapeCacheEntry& r) const {
-        int deltaInt = ShapeCacheEntry::compare(r);
-        if (deltaInt != 0) return deltaInt;
-
-        const RoundRectShapeCacheEntry& rhs = (const RoundRectShapeCacheEntry&) r;
-
-        if (mWidth < rhs.mWidth) return -1;
-        if (mWidth > rhs.mWidth) return +1;
-
-        if (mHeight < rhs.mHeight) return -1;
-        if (mHeight > rhs.mHeight) return +1;
-
-        if (mRx < rhs.mRx) return -1;
-        if (mRx > rhs.mRx) return +1;
-
-        if (mRy < rhs.mRy) return -1;
-        if (mRy > rhs.mRy) return +1;
-
-        return 0;
-    }
-
-private:
-    float mWidth;
-    float mHeight;
-    float mRx;
-    float mRy;
-}; // RoundRectShapeCacheEntry
-
-inline hash_t hash_type(const RoundRectShapeCacheEntry& entry) {
-    return entry.hash();
-}
-
-struct CircleShapeCacheEntry: public ShapeCacheEntry {
-    CircleShapeCacheEntry(float radius, SkPaint* paint):
-            ShapeCacheEntry(ShapeCacheEntry::kShapeCircle, paint) {
-        mRadius = radius;
-    }
-
-    CircleShapeCacheEntry(): ShapeCacheEntry() {
-        mRadius = 0;
-    }
-
-    hash_t hash() const {
-        uint32_t hash = ShapeCacheEntry::hash();
-        hash = JenkinsHashMix(hash, android::hash_type(mRadius));
-        return JenkinsHashWhiten(hash);
-    }
-
-    int compare(const ShapeCacheEntry& r) const {
-        int deltaInt = ShapeCacheEntry::compare(r);
-        if (deltaInt != 0) return deltaInt;
-
-        const CircleShapeCacheEntry& rhs = (const CircleShapeCacheEntry&) r;
-
-        if (mRadius < rhs.mRadius) return -1;
-        if (mRadius > rhs.mRadius) return +1;
-
-        return 0;
-    }
-
-private:
-    float mRadius;
-}; // CircleShapeCacheEntry
-
-inline hash_t hash_type(const CircleShapeCacheEntry& entry) {
-    return entry.hash();
-}
-
-struct OvalShapeCacheEntry: public ShapeCacheEntry {
-    OvalShapeCacheEntry(float width, float height, SkPaint* paint):
-            ShapeCacheEntry(ShapeCacheEntry::kShapeOval, paint) {
-        mWidth = width;
-        mHeight = height;
-    }
-
-    OvalShapeCacheEntry(): ShapeCacheEntry() {
-        mWidth = mHeight = 0;
-    }
-
-    hash_t hash() const {
-        uint32_t hash = ShapeCacheEntry::hash();
-        hash = JenkinsHashMix(hash, android::hash_type(mWidth));
-        hash = JenkinsHashMix(hash, android::hash_type(mHeight));
-        return JenkinsHashWhiten(hash);
-    }
-
-    int compare(const ShapeCacheEntry& r) const {
-        int deltaInt = ShapeCacheEntry::compare(r);
-        if (deltaInt != 0) return deltaInt;
-
-        const OvalShapeCacheEntry& rhs = (const OvalShapeCacheEntry&) r;
-
-        if (mWidth < rhs.mWidth) return -1;
-        if (mWidth > rhs.mWidth) return +1;
-
-        if (mHeight < rhs.mHeight) return -1;
-        if (mHeight > rhs.mHeight) return +1;
-
-        return 0;
-    }
-
-private:
-    float mWidth;
-    float mHeight;
-}; // OvalShapeCacheEntry
-
-inline hash_t hash_type(const OvalShapeCacheEntry& entry) {
-    return entry.hash();
-}
-
-struct RectShapeCacheEntry: public ShapeCacheEntry {
-    RectShapeCacheEntry(float width, float height, SkPaint* paint):
-            ShapeCacheEntry(ShapeCacheEntry::kShapeRect, paint) {
-        mWidth = width;
-        mHeight = height;
-    }
-
-    RectShapeCacheEntry(): ShapeCacheEntry() {
-        mWidth = mHeight = 0;
-    }
-
-    hash_t hash() const {
-        uint32_t hash = ShapeCacheEntry::hash();
-        hash = JenkinsHashMix(hash, android::hash_type(mWidth));
-        hash = JenkinsHashMix(hash, android::hash_type(mHeight));
-        return JenkinsHashWhiten(hash);
-    }
-
-    int compare(const ShapeCacheEntry& r) const {
-        int deltaInt = ShapeCacheEntry::compare(r);
-        if (deltaInt != 0) return deltaInt;
-
-        const RectShapeCacheEntry& rhs = (const RectShapeCacheEntry&) r;
-
-        if (mWidth < rhs.mWidth) return -1;
-        if (mWidth > rhs.mWidth) return +1;
-
-        if (mHeight < rhs.mHeight) return -1;
-        if (mHeight > rhs.mHeight) return +1;
-
-        return 0;
-    }
-
-private:
-    float mWidth;
-    float mHeight;
-}; // RectShapeCacheEntry
-
-inline hash_t hash_type(const RectShapeCacheEntry& entry) {
-    return entry.hash();
-}
-
-struct ArcShapeCacheEntry: public ShapeCacheEntry {
-    ArcShapeCacheEntry(float width, float height, float startAngle, float sweepAngle,
-            bool useCenter, SkPaint* paint):
-            ShapeCacheEntry(ShapeCacheEntry::kShapeArc, paint) {
-        mWidth = width;
-        mHeight = height;
-        mStartAngle = startAngle;
-        mSweepAngle = sweepAngle;
-        mUseCenter = useCenter ? 1 : 0;
-    }
-
-    ArcShapeCacheEntry(): ShapeCacheEntry() {
-        mWidth = 0;
-        mHeight = 0;
-        mStartAngle = 0;
-        mSweepAngle = 0;
-        mUseCenter = 0;
-    }
-
-    hash_t hash() const {
-        uint32_t hash = ShapeCacheEntry::hash();
-        hash = JenkinsHashMix(hash, android::hash_type(mWidth));
-        hash = JenkinsHashMix(hash, android::hash_type(mHeight));
-        hash = JenkinsHashMix(hash, android::hash_type(mStartAngle));
-        hash = JenkinsHashMix(hash, android::hash_type(mSweepAngle));
-        hash = JenkinsHashMix(hash, mUseCenter);
-        return JenkinsHashWhiten(hash);
-    }
-
-    int compare(const ShapeCacheEntry& r) const {
-        int deltaInt = ShapeCacheEntry::compare(r);
-        if (deltaInt != 0) return deltaInt;
-
-        const ArcShapeCacheEntry& rhs = (const ArcShapeCacheEntry&) r;
-
-        if (mWidth < rhs.mWidth) return -1;
-        if (mWidth > rhs.mWidth) return +1;
-
-        if (mHeight < rhs.mHeight) return -1;
-        if (mHeight > rhs.mHeight) return +1;
-
-        if (mStartAngle < rhs.mStartAngle) return -1;
-        if (mStartAngle > rhs.mStartAngle) return +1;
-
-        if (mSweepAngle < rhs.mSweepAngle) return -1;
-        if (mSweepAngle > rhs.mSweepAngle) return +1;
-
-        return mUseCenter - rhs.mUseCenter;
-    }
-
-private:
-    float mWidth;
-    float mHeight;
-    float mStartAngle;
-    float mSweepAngle;
-    uint32_t mUseCenter;
-}; // ArcShapeCacheEntry
-
-inline hash_t hash_type(const ArcShapeCacheEntry& entry) {
-    return entry.hash();
-}
-
-/**
- * A simple LRU shape cache. The cache has a maximum size expressed in bytes.
- * Any texture added to the cache causing the cache to grow beyond the maximum
- * allowed size will also cause the oldest texture to be kicked out.
- */
-template<typename Entry>
-class ShapeCache: public OnEntryRemoved<Entry, PathTexture*> {
-public:
-    ShapeCache(const char* name, const char* propertyName, float defaultSize);
-    ~ShapeCache();
-
-    /**
-     * Used as a callback when an entry is removed from the cache.
-     * Do not invoke directly.
-     */
-    void operator()(Entry& path, PathTexture*& texture);
-
-    /**
-     * Clears the cache. This causes all textures to be deleted.
-     */
-    void clear();
-
-    /**
-     * Sets the maximum size of the cache in bytes.
-     */
-    void setMaxSize(uint32_t maxSize);
-    /**
-     * Returns the maximum size of the cache in bytes.
-     */
-    uint32_t getMaxSize();
-    /**
-     * Returns the current size of the cache in bytes.
-     */
-    uint32_t getSize();
-
-    /**
-     * Trims the contents of the cache, removing items until it's under its
-     * specified limit.
-     *
-     * Trimming is used for caches that support pre-caching from a worker
-     * thread. During pre-caching the maximum limit of the cache can be
-     * exceeded for the duration of the frame. It is therefore required to
-     * trim the cache at the end of the frame to keep the total amount of
-     * memory used under control.
-     *
-     * Only the PathCache currently supports pre-caching.
-     */
-    void trim();
-
-    static void computePathBounds(const SkPath* path, const SkPaint* paint,
-            float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
-        const SkRect& bounds = path->getBounds();
-        computeBounds(bounds, paint, left, top, offset, width, height);
-    }
-
-    static void computeBounds(const SkRect& bounds, const SkPaint* paint,
-            float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
-        const float pathWidth = fmax(bounds.width(), 1.0f);
-        const float pathHeight = fmax(bounds.height(), 1.0f);
-
-        left = bounds.fLeft;
-        top = bounds.fTop;
-
-        offset = (int) floorf(fmax(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f);
-
-        width = uint32_t(pathWidth + offset * 2.0 + 0.5);
-        height = uint32_t(pathHeight + offset * 2.0 + 0.5);
-    }
-
-    static void drawPath(const SkPath *path, const SkPaint* paint, SkBitmap& bitmap,
-            float left, float top, float offset, uint32_t width, uint32_t height) {
-        initBitmap(bitmap, width, height);
-
-        SkPaint pathPaint(*paint);
-        initPaint(pathPaint);
-
-        SkCanvas canvas(bitmap);
-        canvas.translate(-left + offset, -top + offset);
-        canvas.drawPath(*path, pathPaint);
-    }
-
-protected:
-    PathTexture* addTexture(const Entry& entry, const SkPath *path, const SkPaint* paint);
-    PathTexture* addTexture(const Entry& entry, SkBitmap* bitmap);
-    void addTexture(const Entry& entry, SkBitmap* bitmap, PathTexture* texture);
-
-    /**
-     * Ensures there is enough space in the cache for a texture of the specified
-     * dimensions.
-     */
-    void purgeCache(uint32_t width, uint32_t height);
-
-    PathTexture* get(Entry entry) {
-        return mCache.get(entry);
-    }
-
-    void removeTexture(PathTexture* texture);
-
-    bool checkTextureSize(uint32_t width, uint32_t height) {
-        if (width > mMaxTextureSize || height > mMaxTextureSize) {
-            ALOGW("Shape %s too large to be rendered into a texture (%dx%d, max=%dx%d)",
-                    mName, width, height, mMaxTextureSize, mMaxTextureSize);
-            return false;
-        }
-        return true;
-    }
-
-    static PathTexture* createTexture(float left, float top, float offset,
-            uint32_t width, uint32_t height, uint32_t id) {
-        PathTexture* texture = new PathTexture();
-        texture->left = left;
-        texture->top = top;
-        texture->offset = offset;
-        texture->width = width;
-        texture->height = height;
-        texture->generation = id;
-        return texture;
-    }
-
-    static void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) {
-        bitmap.setConfig(SkBitmap::kA8_Config, width, height);
-        bitmap.allocPixels();
-        bitmap.eraseColor(0);
-    }
-
-    static void initPaint(SkPaint& paint) {
-        // Make sure the paint is opaque, color, alpha, filter, etc.
-        // will be applied later when compositing the alpha8 texture
-        paint.setColor(0xff000000);
-        paint.setAlpha(255);
-        paint.setColorFilter(NULL);
-        paint.setMaskFilter(NULL);
-        paint.setShader(NULL);
-        SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode);
-        SkSafeUnref(paint.setXfermode(mode));
-    }
-
-    LruCache<Entry, PathTexture*> mCache;
-    uint32_t mSize;
-    uint32_t mMaxSize;
-    GLuint mMaxTextureSize;
-
-    char* mName;
-    bool mDebugEnabled;
-
-private:
-    /**
-     * Generates the texture from a bitmap into the specified texture structure.
-     */
-    void generateTexture(SkBitmap& bitmap, Texture* texture);
-
-    void init();
-}; // class ShapeCache
-
-class RoundRectShapeCache: public ShapeCache<RoundRectShapeCacheEntry> {
-public:
-    RoundRectShapeCache();
-
-    PathTexture* getRoundRect(float width, float height, float rx, float ry, SkPaint* paint);
-}; // class RoundRectShapeCache
-
-class CircleShapeCache: public ShapeCache<CircleShapeCacheEntry> {
-public:
-    CircleShapeCache();
-
-    PathTexture* getCircle(float radius, SkPaint* paint);
-}; // class CircleShapeCache
-
-class OvalShapeCache: public ShapeCache<OvalShapeCacheEntry> {
-public:
-    OvalShapeCache();
-
-    PathTexture* getOval(float width, float height, SkPaint* paint);
-}; // class OvalShapeCache
-
-class RectShapeCache: public ShapeCache<RectShapeCacheEntry> {
-public:
-    RectShapeCache();
-
-    PathTexture* getRect(float width, float height, SkPaint* paint);
-}; // class RectShapeCache
-
-class ArcShapeCache: public ShapeCache<ArcShapeCacheEntry> {
-public:
-    ArcShapeCache();
-
-    PathTexture* getArc(float width, float height, float startAngle, float sweepAngle,
-            bool useCenter, SkPaint* paint);
-}; // class ArcShapeCache
-
-///////////////////////////////////////////////////////////////////////////////
-// Constructors/destructor
-///////////////////////////////////////////////////////////////////////////////
-
-template<class Entry>
-ShapeCache<Entry>::ShapeCache(const char* name, const char* propertyName, float defaultSize):
-        mCache(LruCache<ShapeCacheEntry, PathTexture*>::kUnlimitedCapacity),
-        mSize(0), mMaxSize(MB(defaultSize)) {
-    char property[PROPERTY_VALUE_MAX];
-    if (property_get(propertyName, property, NULL) > 0) {
-        INIT_LOGD("  Setting %s cache size to %sMB", name, property);
-        setMaxSize(MB(atof(property)));
-    } else {
-        INIT_LOGD("  Using default %s cache size of %.2fMB", name, defaultSize);
-    }
-
-    size_t len = strlen(name);
-    mName = new char[len + 1];
-    strcpy(mName, name);
-    mName[len] = '\0';
-
-    init();
-}
-
-template<class Entry>
-ShapeCache<Entry>::~ShapeCache() {
-    mCache.clear();
-    delete[] mName;
-}
-
-template<class Entry>
-void ShapeCache<Entry>::init() {
-    mCache.setOnEntryRemovedListener(this);
-
-    GLint maxTextureSize;
-    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
-    mMaxTextureSize = maxTextureSize;
-
-    mDebugEnabled = readDebugLevel() & kDebugCaches;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Size management
-///////////////////////////////////////////////////////////////////////////////
-
-template<class Entry>
-uint32_t ShapeCache<Entry>::getSize() {
-    return mSize;
-}
-
-template<class Entry>
-uint32_t ShapeCache<Entry>::getMaxSize() {
-    return mMaxSize;
-}
-
-template<class Entry>
-void ShapeCache<Entry>::setMaxSize(uint32_t maxSize) {
-    mMaxSize = maxSize;
-    while (mSize > mMaxSize) {
-        mCache.removeOldest();
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Callbacks
-///////////////////////////////////////////////////////////////////////////////
-
-template<class Entry>
-void ShapeCache<Entry>::operator()(Entry& path, PathTexture*& texture) {
-    removeTexture(texture);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Caching
-///////////////////////////////////////////////////////////////////////////////
-
-template<class Entry>
-void ShapeCache<Entry>::removeTexture(PathTexture* texture) {
-    if (texture) {
-        const uint32_t size = texture->width * texture->height;
-        mSize -= size;
-
-        SHAPE_LOGD("ShapeCache::callback: delete %s: name, size, mSize = %d, %d, %d",
-                mName, texture->id, size, mSize);
-        if (mDebugEnabled) {
-            ALOGD("Shape %s deleted, size = %d", mName, size);
-        }
-
-        if (texture->id) {
-            glDeleteTextures(1, &texture->id);
-        }
-        delete texture;
-    }
-}
-
-template<class Entry>
-void ShapeCache<Entry>::purgeCache(uint32_t width, uint32_t height) {
-    const uint32_t size = width * height;
-    // Don't even try to cache a bitmap that's bigger than the cache
-    if (size < mMaxSize) {
-        while (mSize + size > mMaxSize) {
-            mCache.removeOldest();
-        }
-    }
-}
-
-template<class Entry>
-void ShapeCache<Entry>::trim() {
-    while (mSize > mMaxSize) {
-        mCache.removeOldest();
-    }
-}
-
-template<class Entry>
-PathTexture* ShapeCache<Entry>::addTexture(const Entry& entry, const SkPath *path,
-        const SkPaint* paint) {
-    ATRACE_CALL();
-
-    float left, top, offset;
-    uint32_t width, height;
-    computePathBounds(path, paint, left, top, offset, width, height);
-
-    if (!checkTextureSize(width, height)) return NULL;
-
-    purgeCache(width, height);
-
-    SkBitmap bitmap;
-    drawPath(path, paint, bitmap, left, top, offset, width, height);
-
-    PathTexture* texture = createTexture(left, top, offset, width, height,
-            path->getGenerationID());
-    addTexture(entry, &bitmap, texture);
-
-    return texture;
-}
-
-template<class Entry>
-void ShapeCache<Entry>::addTexture(const Entry& entry, SkBitmap* bitmap, PathTexture* texture) {
-    generateTexture(*bitmap, texture);
-
-    uint32_t size = texture->width * texture->height;
-    if (size < mMaxSize) {
-        mSize += size;
-        SHAPE_LOGD("ShapeCache::get: create %s: name, size, mSize = %d, %d, %d",
-                mName, texture->id, size, mSize);
-        if (mDebugEnabled) {
-            ALOGD("Shape %s created, size = %d", mName, size);
-        }
-        mCache.put(entry, texture);
-    } else {
-        texture->cleanup = true;
-    }
-}
-
-template<class Entry>
-void ShapeCache<Entry>::clear() {
-    mCache.clear();
-}
-
-template<class Entry>
-void ShapeCache<Entry>::generateTexture(SkBitmap& bitmap, Texture* texture) {
-    SkAutoLockPixels alp(bitmap);
-    if (!bitmap.readyToDraw()) {
-        ALOGE("Cannot generate texture from bitmap");
-        return;
-    }
-
-    glGenTextures(1, &texture->id);
-
-    glBindTexture(GL_TEXTURE_2D, texture->id);
-    // Textures are Alpha8
-    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-
-    texture->blend = true;
-    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0,
-            GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.getPixels());
-
-    texture->setFilter(GL_LINEAR);
-    texture->setWrap(GL_CLAMP_TO_EDGE);
-}
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_SHAPE_CACHE_H
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index 9013fd5..c38eedb 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -411,8 +411,14 @@
 
 void SkiaComposeShader::setupProgram(Program* program, const mat4& modelView,
         const Snapshot& snapshot, GLuint* textureUnit) {
-    mFirst->setupProgram(program, modelView, snapshot, textureUnit);
-    mSecond->setupProgram(program, modelView, snapshot, textureUnit);
+    // Apply this compose shader's local transform and pass it down to
+    // the child shaders. They will in turn apply their local transform
+    // to this matrix.
+    mat4 transform;
+    computeScreenSpaceMatrix(transform, modelView);
+
+    mFirst->setupProgram(program, transform, snapshot, textureUnit);
+    mSecond->setupProgram(program, transform, snapshot, textureUnit);
 }
 
 }; // namespace uirenderer
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 0b3df35..72d249a 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -96,6 +96,7 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.am.BatteryStatsService;
+import com.android.server.connectivity.Nat464Xlat;
 import com.android.server.connectivity.Tethering;
 import com.android.server.connectivity.Vpn;
 import com.android.server.net.BaseNetworkObserver;
@@ -154,6 +155,8 @@
     private boolean mLockdownEnabled;
     private LockdownVpnTracker mLockdownTracker;
 
+    private Nat464Xlat mClat;
+
     /** Lock around {@link #mUidRules} and {@link #mMeteredIfaces}. */
     private Object mRulesLock = new Object();
     /** Currently active network rules by UID. */
@@ -544,9 +547,12 @@
         mVpn = new Vpn(mContext, mVpnCallback, mNetd);
         mVpn.startMonitoring(mContext, mTrackerHandler);
 
+        mClat = new Nat464Xlat(mContext, mNetd, this, mTrackerHandler);
+
         try {
             mNetd.registerObserver(mTethering);
             mNetd.registerObserver(mDataActivityObserver);
+            mNetd.registerObserver(mClat);
         } catch (RemoteException e) {
             loge("Error registering observer :" + e);
         }
@@ -2276,6 +2282,17 @@
             }
         }
 
+        // Update 464xlat state.
+        // TODO: Move to handleConnect()
+        NetworkStateTracker tracker = mNetTrackers[netType];
+        if (mClat.requiresClat(netType, tracker)) {
+            if (mNetTrackers[netType].getNetworkInfo().isConnected()) {
+                mClat.startClat(tracker);
+            } else {
+                mClat.stopClat();
+            }
+        }
+
         // TODO: Temporary notifying upstread change to Tethering.
         //       @see bug/4455071
         /** Notify TetheringService if interface name has been changed. */
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 7686705..2210a18 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -233,6 +233,7 @@
             try {
                 mObservers.getBroadcastItem(i).interfaceStatusChanged(iface, up);
             } catch (RemoteException e) {
+            } catch (RuntimeException e) {
             }
         }
         mObservers.finishBroadcast();
@@ -248,6 +249,7 @@
             try {
                 mObservers.getBroadcastItem(i).interfaceLinkStateChanged(iface, up);
             } catch (RemoteException e) {
+            } catch (RuntimeException e) {
             }
         }
         mObservers.finishBroadcast();
@@ -262,6 +264,7 @@
             try {
                 mObservers.getBroadcastItem(i).interfaceAdded(iface);
             } catch (RemoteException e) {
+            } catch (RuntimeException e) {
             }
         }
         mObservers.finishBroadcast();
@@ -281,6 +284,7 @@
             try {
                 mObservers.getBroadcastItem(i).interfaceRemoved(iface);
             } catch (RemoteException e) {
+            } catch (RuntimeException e) {
             }
         }
         mObservers.finishBroadcast();
@@ -295,6 +299,7 @@
             try {
                 mObservers.getBroadcastItem(i).limitReached(limitName, iface);
             } catch (RemoteException e) {
+            } catch (RuntimeException e) {
             }
         }
         mObservers.finishBroadcast();
@@ -309,6 +314,7 @@
             try {
                 mObservers.getBroadcastItem(i).interfaceClassDataActivityChanged(label, active);
             } catch (RemoteException e) {
+            } catch (RuntimeException e) {
             }
         }
         mObservers.finishBroadcast();
diff --git a/services/java/com/android/server/connectivity/Nat464Xlat.java b/services/java/com/android/server/connectivity/Nat464Xlat.java
new file mode 100644
index 0000000..2884eaf
--- /dev/null
+++ b/services/java/com/android/server/connectivity/Nat464Xlat.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+
+import java.net.Inet4Address;
+
+import android.content.Context;
+import android.net.IConnectivityManager;
+import android.net.InterfaceConfiguration;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.NetworkStateTracker;
+import android.net.NetworkUtils;
+import android.net.RouteInfo;
+import android.os.Handler;
+import android.os.Message;
+import android.os.INetworkManagementService;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.net.BaseNetworkObserver;
+
+/**
+ * @hide
+ *
+ * Class to manage a 464xlat CLAT daemon.
+ */
+public class Nat464Xlat extends BaseNetworkObserver {
+    private Context mContext;
+    private INetworkManagementService mNMService;
+    private IConnectivityManager mConnService;
+    private NetworkStateTracker mTracker;
+    private Handler mHandler;
+
+    // Whether we started clatd and expect it to be running.
+    private boolean mIsStarted;
+    // Whether the clatd interface exists (i.e., clatd is running).
+    private boolean mIsRunning;
+    // The LinkProperties of the clat interface.
+    private LinkProperties mLP;
+
+    // This must match the interface name in clatd.conf.
+    private static final String CLAT_INTERFACE_NAME = "clat4";
+
+    private static final String TAG = "Nat464Xlat";
+
+    public Nat464Xlat(Context context, INetworkManagementService nmService,
+                      IConnectivityManager connService, Handler handler) {
+        mContext = context;
+        mNMService = nmService;
+        mConnService = connService;
+        mHandler = handler;
+
+        mIsStarted = false;
+        mIsRunning = false;
+        mLP = new LinkProperties();
+    }
+
+    /**
+     * Determines whether an interface requires clat.
+     * @param netType the network type (one of the
+     *   android.net.ConnectivityManager.TYPE_* constants)
+     * @param tracker the NetworkStateTracker corresponding to the network type.
+     * @return true if the interface requires clat, false otherwise.
+     */
+    public boolean requiresClat(int netType, NetworkStateTracker tracker) {
+        LinkProperties lp = tracker.getLinkProperties();
+        // Only support clat on mobile for now.
+        Slog.d(TAG, "requiresClat: netType=" + netType + ", hasIPv4Address=" +
+               lp.hasIPv4Address());
+        return netType == TYPE_MOBILE && !lp.hasIPv4Address();
+    }
+
+    /**
+     * Starts the clat daemon.
+     * @param lp The link properties of the interface to start clatd on.
+     */
+    public void startClat(NetworkStateTracker tracker) {
+        if (mIsStarted) {
+            Slog.e(TAG, "startClat: already started");
+            return;
+        }
+        mTracker = tracker;
+        LinkProperties lp = mTracker.getLinkProperties();
+        String iface = lp.getInterfaceName();
+        Slog.i(TAG, "Starting clatd on " + iface + ", lp=" + lp);
+        try {
+            mNMService.startClatd(iface);
+        } catch(RemoteException e) {
+            Slog.e(TAG, "Error starting clat daemon: " + e);
+        }
+        mIsStarted = true;
+    }
+
+    /**
+     * Stops the clat daemon.
+     */
+    public void stopClat() {
+        if (mIsStarted) {
+            Slog.i(TAG, "Stopping clatd");
+            try {
+                mNMService.stopClatd();
+            } catch(RemoteException e) {
+                Slog.e(TAG, "Error stopping clat daemon: " + e);
+            }
+            mIsStarted = false;
+            mIsRunning = false;
+            mTracker = null;
+            mLP.clear();
+        } else {
+            Slog.e(TAG, "stopClat: already stopped");
+        }
+    }
+
+    public boolean isStarted() {
+        return mIsStarted;
+    }
+
+    public boolean isRunning() {
+        return mIsRunning;
+    }
+
+    @Override
+    public void interfaceAdded(String iface) {
+        if (iface.equals(CLAT_INTERFACE_NAME)) {
+            Slog.i(TAG, "interface " + CLAT_INTERFACE_NAME +
+                   " added, mIsRunning = " + mIsRunning + " -> true");
+            mIsRunning = true;
+
+            // Get the network configuration of the clat interface, store it
+            // in our link properties, and stack it on top of the interface
+            // it's running on.
+            try {
+                InterfaceConfiguration config = mNMService.getInterfaceConfig(iface);
+                mLP.clear();
+                mLP.setInterfaceName(iface);
+                RouteInfo ipv4Default = new RouteInfo(new LinkAddress(Inet4Address.ANY, 0), null,
+                                                      iface);
+                mLP.addRoute(ipv4Default);
+                mLP.addLinkAddress(config.getLinkAddress());
+                mTracker.addStackedLink(mLP);
+                Slog.i(TAG, "Adding stacked link. tracker LP: " +
+                       mTracker.getLinkProperties());
+            } catch(RemoteException e) {
+                Slog.e(TAG, "Error getting link properties: " + e);
+            }
+
+            // Inform ConnectivityService that things have changed.
+            Message msg = mHandler.obtainMessage(
+                NetworkStateTracker.EVENT_CONFIGURATION_CHANGED,
+                mTracker.getNetworkInfo());
+            Slog.i(TAG, "sending message to ConnectivityService: " + msg);
+            msg.sendToTarget();
+        }
+    }
+
+    @Override
+    public void interfaceRemoved(String iface) {
+        if (iface == CLAT_INTERFACE_NAME) {
+            if (mIsRunning) {
+                NetworkUtils.resetConnections(
+                    CLAT_INTERFACE_NAME,
+                    NetworkUtils.RESET_IPV4_ADDRESSES);
+            }
+            Slog.i(TAG, "interface " + CLAT_INTERFACE_NAME +
+                   " removed, mIsRunning = " + mIsRunning + " -> false");
+            mIsRunning = false;
+            mTracker.removeStackedLink(mLP);
+            mLP.clear();
+            Slog.i(TAG, "mLP = " + mLP);
+        }
+    }
+};
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index e4a7ead..32f39b7 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -35,6 +35,7 @@
 import android.net.LinkProperties;
 import android.net.NetworkInfo;
 import android.net.NetworkUtils;
+import android.net.RouteInfo;
 import android.os.Binder;
 import android.os.HandlerThread;
 import android.os.IBinder;
@@ -1345,7 +1346,21 @@
                         linkProperties = mConnService.getLinkProperties(upType);
                     } catch (RemoteException e) { }
                     if (linkProperties != null) {
-                        iface = linkProperties.getInterfaceName();
+                        // Find the interface with the default IPv4 route. It may be the
+                        // interface described by linkProperties, or one of the interfaces
+                        // stacked on top of it.
+                        Log.i(TAG, "Finding IPv4 upstream interface on: " + linkProperties);
+                        RouteInfo ipv4Default = RouteInfo.selectBestRoute(
+                            linkProperties.getAllRoutes(), Inet4Address.ANY);
+                        if (ipv4Default != null) {
+                            iface = ipv4Default.getInterface();
+                            Log.i(TAG, "Found interface " + ipv4Default.getInterface());
+                        } else {
+                            Log.i(TAG, "No IPv4 upstream interface, giving up.");
+                        }
+                    }
+
+                    if (iface != null) {
                         String[] dnsServers = mDefaultDnsServers;
                         Collection<InetAddress> dnses = linkProperties.getDnses();
                         if (dnses != null) {
diff --git a/services/java/com/android/server/wm/ScreenRotationAnimation.java b/services/java/com/android/server/wm/ScreenRotationAnimation.java
index 3708df2..b2fbec1 100644
--- a/services/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -234,6 +234,7 @@
                 mSurfaceControl.setLayer(FREEZE_LAYER + 1);
                 mSurfaceControl.setAlpha(0);
                 mSurfaceControl.show();
+                sur.destroy();
             } catch (SurfaceControl.OutOfResourcesException e) {
                 Slog.w(TAG, "Unable to allocate freeze surface", e);
             }
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index cfefadd..56f4de5 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -1481,7 +1481,11 @@
                     pos++;
                 }
                 if (pos >= N) {
-                    // All is good!
+                    // Z order is good.
+                    // The IM target window may be changed, so update the mTargetAppToken.
+                    if (imWin != null) {
+                        imWin.mTargetAppToken = mInputMethodTarget.mAppToken;
+                    }
                     return false;
                 }
             }
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MoreShadersActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/MoreShadersActivity.java
index 02cb4b6..1847f43 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/MoreShadersActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/MoreShadersActivity.java
@@ -57,6 +57,7 @@
         private Paint mLargePaint;
         private BitmapShader mScaled2Shader;
         private ColorFilter mColorFilter;
+        private final Matrix mMtx1;
 
         ShadersView(Context c) {
             super(c);
@@ -70,7 +71,7 @@
             mScaledShader = new BitmapShader(texture, Shader.TileMode.MIRROR,
                     Shader.TileMode.MIRROR);
             Matrix m2 = new Matrix();
-            m2.setScale(0.5f, 0.5f);
+            m2.setScale(0.1f, 0.1f);
             mScaledShader.setLocalMatrix(m2);
             
             mScaled2Shader = new BitmapShader(texture, Shader.TileMode.MIRROR,
@@ -81,12 +82,20 @@
 
             mHorGradient = new LinearGradient(0.0f, 0.0f, mDrawWidth, 0.0f,
                     Color.RED, 0x7f00ff00, Shader.TileMode.CLAMP);
-            
+            Matrix m4 = new Matrix();
+            m4.setScale(0.5f, 0.5f);
+            mHorGradient.setLocalMatrix(m4);
+
             mVertGradient = new LinearGradient(0.0f, 0.0f, 0.0f, mDrawHeight / 2.0f,
                     Color.YELLOW, Color.MAGENTA, Shader.TileMode.MIRROR);
 
             mComposeShader = new ComposeShader(mScaledShader, mHorGradient,
                     PorterDuff.Mode.SRC_OVER);
+            mMtx1 = new Matrix();
+            mMtx1.setTranslate(mTexWidth / 2.0f, mTexHeight / 2.0f);
+            mMtx1.postRotate(45, 0, 0);
+            mComposeShader.setLocalMatrix(mMtx1);
+
             mCompose2Shader = new ComposeShader(mHorGradient, mScaledShader,
                     PorterDuff.Mode.SRC_OUT);
 
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ShapesActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ShapesActivity.java
index 97e5526..61dca78 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ShapesActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ShapesActivity.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Paint;
+import android.graphics.Path;
 import android.graphics.RectF;
 import android.os.Bundle;
 import android.view.View;
@@ -34,12 +35,13 @@
     }
 
     static class ShapesView extends View {
-        private Paint mNormalPaint;
-        private Paint mStrokePaint;
-        private Paint mFillPaint;
-        private RectF mRect;
-        private RectF mOval;
-        private RectF mArc;
+        private final Paint mNormalPaint;
+        private final Paint mStrokePaint;
+        private final Paint mFillPaint;
+        private final RectF mRect;
+        private final RectF mOval;
+        private final RectF mArc;
+        private final Path mTriangle;
 
         ShapesView(Context c) {
             super(c);
@@ -65,6 +67,12 @@
 
             mOval = new RectF(0.0f, 0.0f, 80.0f, 45.0f);
             mArc = new RectF(0.0f, 0.0f, 100.0f, 120.0f);
+
+            mTriangle = new Path();
+            mTriangle.moveTo(0.0f, 90.0f);
+            mTriangle.lineTo(45.0f, 0.0f);
+            mTriangle.lineTo(90.0f, 90.0f);
+            mTriangle.close();
         }
 
         @Override
@@ -136,6 +144,17 @@
             canvas.translate(0.0f, 110.0f);
             canvas.drawArc(mArc, 30.0f, 100.0f, false, mFillPaint);
             canvas.restore();
+
+            canvas.save();
+            canvas.translate(50.0f, 400.0f);
+            canvas.drawPath(mTriangle, mNormalPaint);
+
+            canvas.translate(110.0f, 0.0f);
+            canvas.drawPath(mTriangle, mStrokePaint);
+
+            canvas.translate(110.0f, 0.0f);
+            canvas.drawPath(mTriangle, mFillPaint);
+            canvas.restore();
         }
     }
 }
diff --git a/tests/SharedLibrary/lib/src/com/google/android/test/shared_library/SharedLibraryMain.java b/tests/SharedLibrary/lib/src/com/google/android/test/shared_library/SharedLibraryMain.java
index c1cd925..2c61b77 100644
--- a/tests/SharedLibrary/lib/src/com/google/android/test/shared_library/SharedLibraryMain.java
+++ b/tests/SharedLibrary/lib/src/com/google/android/test/shared_library/SharedLibraryMain.java
@@ -17,16 +17,14 @@
 package com.google.android.test.shared_library;
 
 import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
+import android.app.Fragment;
+import android.app.FragmentManager;
 import android.content.Context;
-import android.content.DialogInterface;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.res.Resources;
 
 public class SharedLibraryMain {
-    private static String LIBRARY_PACKAGE = "com.google.android.test.shared_library";
+    static String LIBRARY_PACKAGE = "com.google.android.test.shared_library";
 
     /**
      * Base version of the library.
@@ -38,6 +36,9 @@
      */
     public static int VERSION_SECOND = 2;
 
+    /**
+     * Return the version number of the currently installed library.
+     */
     public static int getVersion(Context context) {
         PackageInfo pi = null;
         try {
@@ -48,40 +49,34 @@
         }
     }
 
-    public static void ensureVersion(Activity activity, int minVersion) {
+    /**
+     * Check that the library's version is at least the given minimum version,
+     * displaying a dialog to have the user install an update if that is not true.
+     * The dialog is displayed as a DialogFragment in your activity if a newer
+     * version is needed.  If a newer version is needed, false is returned.
+     */
+    public static boolean ensureVersion(final Activity activity, int minVersion) {
+        final FragmentManager fm = activity.getFragmentManager();
+        final String dialogTag = LIBRARY_PACKAGE + ":version";
+        Fragment curDialog = fm.findFragmentByTag(dialogTag);
+
         if (getVersion(activity) >= minVersion) {
-            return;
+            // Library version is sufficient.  Make sure any version dialog
+            // we had shown is removed before returning.
+            if (curDialog != null) {
+                fm.beginTransaction().remove(curDialog).commitAllowingStateLoss();
+            }
+            return true;
         }
 
-        // The current version of the library does not meet the required version.  Show
-        // a dialog to inform the user and have them update to the current version.
-        // Note that updating the library will be necessity mean killing the current
-        // application (so it can be re-started with the new version, so there is no
-        // reason to return a result here.
-        final Context context;
-        try {
-            context = activity.createPackageContext(LIBRARY_PACKAGE, 0);
-        } catch (PackageManager.NameNotFoundException e) {
-            throw new IllegalStateException("Can't find my package!", e);
+        // The current version of the library does not meet the required version.
+        // If we don't already have a version dialog displayed, display it now.
+        if (curDialog == null) {
+            curDialog = new VersionDialog();
+            fm.beginTransaction().add(curDialog, dialogTag).commitAllowingStateLoss();
         }
 
-        // Display the dialog.  Note that we don't need to deal with activity lifecycle
-        // stuff because if the activity gets recreated, it will first call through to
-        // ensureVersion(), causing us to either re-display the dialog if needed or let
-        // it now proceed.
-        final Resources res = context.getResources();
-        AlertDialog.Builder builder = new AlertDialog.Builder(activity);
-        builder.setTitle(res.getText(R.string.upgrade_title));
-        builder.setMessage(res.getString(R.string.upgrade_body,
-                activity.getApplicationInfo().loadLabel(activity.getPackageManager()),
-                context.getApplicationInfo().loadLabel(context.getPackageManager())));
-        builder.setPositiveButton(res.getText(R.string.upgrade_button),
-                new Dialog.OnClickListener() {
-                    @Override
-                    public void onClick(DialogInterface dialog, int which) {
-                        // Launch play store.
-                    }
-                });
-        builder.show();
+        // Tell the caller that the current version is not sufficient.
+        return false;
     }
 }
diff --git a/tests/SharedLibrary/lib/src/com/google/android/test/shared_library/VersionDialog.java b/tests/SharedLibrary/lib/src/com/google/android/test/shared_library/VersionDialog.java
new file mode 100644
index 0000000..f457532
--- /dev/null
+++ b/tests/SharedLibrary/lib/src/com/google/android/test/shared_library/VersionDialog.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2013 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.google.android.test.shared_library;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.Bundle;
+
+/**
+ * This is the dialog we show when the library's version is older than
+ * the version the app needs.
+ */
+public class VersionDialog extends DialogFragment {
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        final Activity activity = getActivity();
+
+        // Need to use our library's resources for showing the dialog.
+        final Context context;
+        try {
+            context = activity.createPackageContext(SharedLibraryMain.LIBRARY_PACKAGE, 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new IllegalStateException("Can't find my package!", e);
+        }
+
+        final Resources res = context.getResources();
+        AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+        builder.setTitle(res.getText(R.string.upgrade_title));
+        builder.setMessage(res.getString(R.string.upgrade_body,
+                activity.getApplicationInfo().loadLabel(activity.getPackageManager()),
+                context.getApplicationInfo().loadLabel(context.getPackageManager())));
+        builder.setPositiveButton(res.getText(R.string.upgrade_button),
+                new Dialog.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        // Launch play store into the details of our app.
+                        try {
+                            activity.startActivity(new Intent(Intent.ACTION_VIEW,
+                                    Uri.parse("market://details?id="
+                                            + SharedLibraryMain.LIBRARY_PACKAGE)));
+                        } catch (android.content.ActivityNotFoundException anfe) {
+                            activity.startActivity(new Intent(Intent.ACTION_VIEW,
+                                    Uri.parse("http://play.google.com/store/apps/details?id="
+                                            + SharedLibraryMain.LIBRARY_PACKAGE)));
+                        }
+                    }
+                });
+        return builder.create();
+    }
+}