Merge "App ops: track system windows, monitoring changes."
diff --git a/api/current.txt b/api/current.txt
index d1363db..0569b9d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -26305,12 +26305,15 @@
     method public java.lang.CharSequence getPackageName();
     method public android.view.accessibility.AccessibilityNodeInfo getParent();
     method public java.lang.CharSequence getText();
+    method public int getTextSelectionEnd();
+    method public int getTextSelectionStart();
     method public java.lang.CharSequence getViewIdResourceName();
     method public int getWindowId();
     method public boolean isAccessibilityFocused();
     method public boolean isCheckable();
     method public boolean isChecked();
     method public boolean isClickable();
+    method public boolean isEditable();
     method public boolean isEnabled();
     method public boolean isFocusable();
     method public boolean isFocused();
@@ -26335,6 +26338,7 @@
     method public void setClassName(java.lang.CharSequence);
     method public void setClickable(boolean);
     method public void setContentDescription(java.lang.CharSequence);
+    method public void setEditable(boolean);
     method public void setEnabled(boolean);
     method public void setFocusable(boolean);
     method public void setFocused(boolean);
@@ -26353,6 +26357,7 @@
     method public void setSource(android.view.View);
     method public void setSource(android.view.View, int);
     method public void setText(java.lang.CharSequence);
+    method public void setTextSelection(int, int);
     method public void setViewIdResourceName(java.lang.CharSequence);
     method public void setVisibleToUser(boolean);
     method public void writeToParcel(android.os.Parcel, int);
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 50c508c..aca4f9c 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1153,6 +1153,7 @@
             IActivityController watcher = IActivityController.Stub.asInterface(
                     data.readStrongBinder());
             setActivityController(watcher);
+            reply.writeNoException();
             return true;
         }
 
diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java
index fd0f0bf..267555a 100644
--- a/core/java/android/app/LoaderManager.java
+++ b/core/java/android/app/LoaderManager.java
@@ -833,6 +833,7 @@
             for (int i = mLoaders.size()-1; i >= 0; i--) {
                 mLoaders.valueAt(i).destroy();
             }
+            mLoaders.clear();
         }
         
         if (DEBUG) Log.v(TAG, "Destroying Inactive in " + this);
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index 6f7c1f3..0017c46a 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -216,6 +216,13 @@
         setJournalSizeLimit();
         setAutoCheckpointInterval();
         setLocaleFromConfiguration();
+
+        // Register custom functions.
+        final int functionCount = mConfiguration.customFunctions.size();
+        for (int i = 0; i < functionCount; i++) {
+            SQLiteCustomFunction function = mConfiguration.customFunctions.get(i);
+            nativeRegisterCustomFunction(mConnectionPtr, function);
+        }
     }
 
     private void dispose(boolean finalized) {
@@ -974,7 +981,7 @@
         if (count != statement.mNumParameters) {
             throw new SQLiteBindOrColumnIndexOutOfRangeException(
                     "Expected " + statement.mNumParameters + " bind arguments but "
-                    + bindArgs.length + " were provided.");
+                    + count + " were provided.");
         }
         if (count == 0) {
             return;
diff --git a/core/java/android/database/sqlite/SQLiteSession.java b/core/java/android/database/sqlite/SQLiteSession.java
index beb5b3a..d80ab1f 100644
--- a/core/java/android/database/sqlite/SQLiteSession.java
+++ b/core/java/android/database/sqlite/SQLiteSession.java
@@ -926,7 +926,7 @@
     }
 
     private void throwIfNestedTransaction() {
-        if (mTransactionStack == null && mTransactionStack.mParent != null) {
+        if (hasNestedTransaction()) {
             throw new IllegalStateException("Cannot perform this operation because "
                     + "a nested transaction is in progress.");
         }
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index b8ad818..c0d2fae 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -1314,56 +1314,4 @@
             return mLegacySensorManager;
         }
     }
-
-    /**
-     * Sensor event pool implementation.
-     * @hide
-     */
-    protected static final class SensorEventPool {
-        private final int mPoolSize;
-        private final SensorEvent mPool[];
-        private int mNumItemsInPool;
-
-        private SensorEvent createSensorEvent() {
-            // maximal size for all legacy events is 3
-            return new SensorEvent(3);
-        }
-
-        SensorEventPool(int poolSize) {
-            mPoolSize = poolSize;
-            mNumItemsInPool = poolSize;
-            mPool = new SensorEvent[poolSize];
-        }
-
-        SensorEvent getFromPool() {
-            SensorEvent t = null;
-            synchronized (this) {
-                if (mNumItemsInPool > 0) {
-                    // remove the "top" item from the pool
-                    final int index = mPoolSize - mNumItemsInPool;
-                    t = mPool[index];
-                    mPool[index] = null;
-                    mNumItemsInPool--;
-                }
-            }
-            if (t == null) {
-                // the pool was empty or this item was removed from the pool for
-                // the first time. In any case, we need to create a new item.
-                t = createSensorEvent();
-            }
-            return t;
-        }
-
-        void returnToPool(SensorEvent t) {
-            synchronized (this) {
-                // is there space left in the pool?
-                if (mNumItemsInPool < mPoolSize) {
-                    // if so, return the item to the pool
-                    mNumItemsInPool++;
-                    final int index = mPoolSize - mNumItemsInPool;
-                    mPool[index] = t;
-                }
-            }
-        }
-    }
 }
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 7375e7d..9591631 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -16,18 +16,19 @@
 
 package android.hardware;
 
-import android.os.Looper;
-import android.os.Process;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+
+import dalvik.system.CloseGuard;
+
 import android.os.Handler;
-import android.os.Message;
-import android.util.Log;
+import android.os.Looper;
+import android.os.MessageQueue;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
 
-import java.util.ArrayList;
-import java.util.List;
-
 /**
  * Sensor manager implementation that communicates with the built-in
  * system sensors.
@@ -35,236 +36,43 @@
  * @hide
  */
 public class SystemSensorManager extends SensorManager {
-    private static final int SENSOR_DISABLE = -1;
-    private static boolean sSensorModuleInitialized = false;
-    private static ArrayList<Sensor> sFullSensorsList = new ArrayList<Sensor>();
-    /* The thread and the sensor list are global to the process
-     * but the actual thread is spawned on demand */
-    private static SensorThread sSensorThread;
-    private static int sQueue;
+    private static native void nativeClassInit();
+    private static native int nativeGetNextSensor(Sensor sensor, int next);
 
-    // Used within this module from outside SensorManager, don't make private
-    static SparseArray<Sensor> sHandleToSensor = new SparseArray<Sensor>();
-    static final ArrayList<ListenerDelegate> sListeners =
-        new ArrayList<ListenerDelegate>();
+    private static boolean sSensorModuleInitialized = false;
+    private static final Object sSensorModuleLock = new Object();
+    private static final ArrayList<Sensor> sFullSensorsList = new ArrayList<Sensor>();
+    private static final SparseArray<Sensor> sHandleToSensor = new SparseArray<Sensor>();
+
+    // Listener list
+    private final ArrayList<SensorEventListenerSensorPair> mListenerDelegates = new ArrayList<SensorEventListenerSensorPair>();
 
     // Common pool of sensor events.
-    static SensorEventPool sPool;
+    private static SensorEventPool sPool;
 
     // Looper associated with the context in which this instance was created.
-    final Looper mMainLooper;
+    private final Looper mMainLooper;
 
-    /*-----------------------------------------------------------------------*/
+    // maps a SensorEventListener to a SensorEventQueue
+    private final Hashtable<SensorEventListener, SensorEventQueue> mSensorEventQueueMap;
 
-    static private class SensorThread {
-
-        Thread mThread;
-        boolean mSensorsReady;
-
-        SensorThread() {
-        }
-
-        @Override
-        protected void finalize() {
-        }
-
-        // must be called with sListeners lock
-        boolean startLocked() {
-            try {
-                if (mThread == null) {
-                    mSensorsReady = false;
-                    SensorThreadRunnable runnable = new SensorThreadRunnable();
-                    Thread thread = new Thread(runnable, SensorThread.class.getName());
-                    thread.start();
-                    synchronized (runnable) {
-                        while (mSensorsReady == false) {
-                            runnable.wait();
-                        }
-                    }
-                    mThread = thread;
-                }
-            } catch (InterruptedException e) {
-            }
-            return mThread == null ? false : true;
-        }
-
-        private class SensorThreadRunnable implements Runnable {
-            SensorThreadRunnable() {
-            }
-
-            private boolean open() {
-                // NOTE: this cannot synchronize on sListeners, since
-                // it's held in the main thread at least until we
-                // return from here.
-                sQueue = sensors_create_queue();
-                return true;
-            }
-
-            public void run() {
-                //Log.d(TAG, "entering main sensor thread");
-                final float[] values = new float[3];
-                final int[] status = new int[1];
-                final long timestamp[] = new long[1];
-                Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
-
-                if (!open()) {
-                    return;
-                }
-
-                synchronized (this) {
-                    // we've open the driver, we're ready to open the sensors
-                    mSensorsReady = true;
-                    this.notify();
-                }
-
-                while (true) {
-                    // wait for an event
-                    final int sensor = sensors_data_poll(sQueue, values, status, timestamp);
-
-                    int accuracy = status[0];
-                    synchronized (sListeners) {
-                        if (sensor == -1 || sListeners.isEmpty()) {
-                            // we lost the connection to the event stream. this happens
-                            // when the last listener is removed or if there is an error
-                            if (sensor == -1 && !sListeners.isEmpty()) {
-                                // log a warning in case of abnormal termination
-                                Log.e(TAG, "_sensors_data_poll() failed, we bail out: sensors=" + sensor);
-                            }
-                            // we have no more listeners or polling failed, terminate the thread
-                            sensors_destroy_queue(sQueue);
-                            sQueue = 0;
-                            mThread = null;
-                            break;
-                        }
-                        final Sensor sensorObject = sHandleToSensor.get(sensor);
-                        if (sensorObject != null) {
-                            // report the sensor event to all listeners that
-                            // care about it.
-                            final int size = sListeners.size();
-                            for (int i=0 ; i<size ; i++) {
-                                ListenerDelegate listener = sListeners.get(i);
-                                if (listener.hasSensor(sensorObject)) {
-                                    // this is asynchronous (okay to call
-                                    // with sListeners lock held).
-                                    listener.onSensorChangedLocked(sensorObject,
-                                            values, timestamp, accuracy);
-                                }
-                            }
-                        }
-                    }
-                }
-                //Log.d(TAG, "exiting main sensor thread");
-            }
-        }
-    }
-
-    /*-----------------------------------------------------------------------*/
-
-    private class ListenerDelegate {
-        private final SensorEventListener mSensorEventListener;
-        private final ArrayList<Sensor> mSensorList = new ArrayList<Sensor>();
-        private final Handler mHandler;
-        public SparseBooleanArray mSensors = new SparseBooleanArray();
-        public SparseBooleanArray mFirstEvent = new SparseBooleanArray();
-        public SparseIntArray mSensorAccuracies = new SparseIntArray();
-
-        ListenerDelegate(SensorEventListener listener, Sensor sensor, Handler handler) {
-            mSensorEventListener = listener;
-            Looper looper = (handler != null) ? handler.getLooper() : mMainLooper;
-            // currently we create one Handler instance per listener, but we could
-            // have one per looper (we'd need to pass the ListenerDelegate
-            // instance to handleMessage and keep track of them separately).
-            mHandler = new Handler(looper) {
-                @Override
-                public void handleMessage(Message msg) {
-                    final SensorEvent t = (SensorEvent)msg.obj;
-                    final int handle = t.sensor.getHandle();
-
-                    switch (t.sensor.getType()) {
-                        // Only report accuracy for sensors that support it.
-                        case Sensor.TYPE_MAGNETIC_FIELD:
-                        case Sensor.TYPE_ORIENTATION:
-                            // call onAccuracyChanged() only if the value changes
-                            final int accuracy = mSensorAccuracies.get(handle);
-                            if ((t.accuracy >= 0) && (accuracy != t.accuracy)) {
-                                mSensorAccuracies.put(handle, t.accuracy);
-                                mSensorEventListener.onAccuracyChanged(t.sensor, t.accuracy);
-                            }
-                            break;
-                        default:
-                            // For other sensors, just report the accuracy once
-                            if (mFirstEvent.get(handle) == false) {
-                                mFirstEvent.put(handle, true);
-                                mSensorEventListener.onAccuracyChanged(
-                                        t.sensor, SENSOR_STATUS_ACCURACY_HIGH);
-                            }
-                            break;
-                    }
-
-                    mSensorEventListener.onSensorChanged(t);
-                    sPool.returnToPool(t);
-                }
-            };
-            addSensor(sensor);
-        }
-
-        Object getListener() {
-            return mSensorEventListener;
-        }
-
-        void addSensor(Sensor sensor) {
-            mSensors.put(sensor.getHandle(), true);
-            mSensorList.add(sensor);
-        }
-        int removeSensor(Sensor sensor) {
-            mSensors.delete(sensor.getHandle());
-            mSensorList.remove(sensor);
-            return mSensors.size();
-        }
-        boolean hasSensor(Sensor sensor) {
-            return mSensors.get(sensor.getHandle());
-        }
-        List<Sensor> getSensors() {
-            return mSensorList;
-        }
-
-        void onSensorChangedLocked(Sensor sensor, float[] values, long[] timestamp, int accuracy) {
-            SensorEvent t = sPool.getFromPool();
-            final float[] v = t.values;
-            v[0] = values[0];
-            v[1] = values[1];
-            v[2] = values[2];
-            t.timestamp = timestamp[0];
-            t.accuracy = accuracy;
-            t.sensor = sensor;
-            Message msg = Message.obtain();
-            msg.what = 0;
-            msg.obj = t;
-            msg.setAsynchronous(true);
-            mHandler.sendMessage(msg);
-        }
-    }
-
-    /**
-     * {@hide}
-     */
+    /** {@hide} */
     public SystemSensorManager(Looper mainLooper) {
         mMainLooper = mainLooper;
+        mSensorEventQueueMap = new Hashtable<SensorEventListener, SensorEventQueue>();
 
-        synchronized(sListeners) {
+        synchronized(sSensorModuleLock) {
             if (!sSensorModuleInitialized) {
                 sSensorModuleInitialized = true;
 
                 nativeClassInit();
 
                 // initialize the sensor list
-                sensors_module_init();
                 final ArrayList<Sensor> fullList = sFullSensorsList;
                 int i = 0;
                 do {
                     Sensor sensor = new Sensor();
-                    i = sensors_module_get_next_sensor(sensor, i);
-
+                    i = nativeGetNextSensor(sensor, i);
                     if (i>=0) {
                         //Log.d(TAG, "found sensor: " + sensor.getName() +
                         //        ", handle=" + sensor.getHandle());
@@ -274,126 +82,304 @@
                 } while (i>0);
 
                 sPool = new SensorEventPool( sFullSensorsList.size()*2 );
-                sSensorThread = new SensorThread();
             }
         }
     }
 
+
     /** @hide */
     @Override
     protected List<Sensor> getFullSensorList() {
         return sFullSensorsList;
     }
 
-    private boolean enableSensorLocked(Sensor sensor, int delay) {
-        boolean result = false;
-        for (ListenerDelegate i : sListeners) {
-            if (i.hasSensor(sensor)) {
-                String name = sensor.getName();
-                int handle = sensor.getHandle();
-                result = sensors_enable_sensor(sQueue, name, handle, delay);
-                break;
-            }
-        }
-        return result;
-    }
-
-    private boolean disableSensorLocked(Sensor sensor) {
-        for (ListenerDelegate i : sListeners) {
-            if (i.hasSensor(sensor)) {
-                // not an error, it's just that this sensor is still in use
-                return true;
-            }
-        }
-        String name = sensor.getName();
-        int handle = sensor.getHandle();
-        return sensors_enable_sensor(sQueue, name, handle, SENSOR_DISABLE);
-    }
 
     /** @hide */
     @Override
     protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor,
-            int delay, Handler handler) {
-        boolean result = true;
-        synchronized (sListeners) {
-            // look for this listener in our list
-            ListenerDelegate l = null;
-            for (ListenerDelegate i : sListeners) {
-                if (i.getListener() == listener) {
-                    l = i;
-                    break;
+            int delay, Handler handler)
+    {
+        // Invariants to preserve:
+        // - one Looper per SensorEventListener
+        // - one Looper per SensorEventQueue
+        // We map SensorEventListeners to a SensorEventQueue, which holds the looper
+
+        if (sensor == null) throw new NullPointerException("sensor cannot be null");
+
+        boolean result;
+        synchronized (mSensorEventQueueMap) {
+            // check if we already have this SensorEventListener, Sensor pair
+            // registered -- if so, we ignore the register. This is not ideal
+            // but this is what the implementation has always been doing.
+            for (SensorEventListenerSensorPair l : mListenerDelegates) {
+                if (l.isSameListenerSensorPair(listener, sensor)) {
+                    // already added, just return silently.
+                    return true;
                 }
             }
 
-            // if we don't find it, add it to the list
-            if (l == null) {
-                l = new ListenerDelegate(listener, sensor, handler);
-                sListeners.add(l);
-                // if the list is not empty, start our main thread
-                if (!sListeners.isEmpty()) {
-                    if (sSensorThread.startLocked()) {
-                        if (!enableSensorLocked(sensor, delay)) {
-                            // oops. there was an error
-                            sListeners.remove(l);
-                            result = false;
-                        }
-                    } else {
-                        // there was an error, remove the listener
-                        sListeners.remove(l);
-                        result = false;
-                    }
-                } else {
-                    // weird, we couldn't add the listener
-                    result = false;
+            // now find the SensorEventQueue associated to this listener
+            SensorEventQueue queue = mSensorEventQueueMap.get(listener);
+            if (queue != null) {
+                result = queue.addSensor(sensor, delay);
+                if (result) {
+                    // create a new ListenerDelegate for this pair
+                    mListenerDelegates.add(new SensorEventListenerSensorPair(listener, sensor));
                 }
-            } else if (!l.hasSensor(sensor)) {
-                l.addSensor(sensor);
-                if (!enableSensorLocked(sensor, delay)) {
-                    // oops. there was an error
-                    l.removeSensor(sensor);
-                    result = false;
+            } else {
+                Looper looper = (handler != null) ? handler.getLooper() : mMainLooper;
+                queue = new SensorEventQueue(listener, looper.getQueue());
+                result = queue.addSensor(sensor, delay);
+                if (result) {
+                    // create a new ListenerDelegate for this pair
+                    mListenerDelegates.add(new SensorEventListenerSensorPair(listener, sensor));
+                    mSensorEventQueueMap.put(listener, queue);
+                } else {
+                    queue.dispose();
                 }
             }
         }
-
         return result;
     }
 
     /** @hide */
     @Override
     protected void unregisterListenerImpl(SensorEventListener listener, Sensor sensor) {
-        synchronized (sListeners) {
-            final int size = sListeners.size();
-            for (int i=0 ; i<size ; i++) {
-                ListenerDelegate l = sListeners.get(i);
-                if (l.getListener() == listener) {
-                    if (sensor == null) {
-                        sListeners.remove(i);
-                        // disable all sensors for this listener
-                        for (Sensor s : l.getSensors()) {
-                            disableSensorLocked(s);
-                        }
-                    // Check if the ListenerDelegate has the sensor it is trying to unregister.
-                    } else if (l.hasSensor(sensor) && l.removeSensor(sensor) == 0) {
-                        // if we have no more sensors enabled on this listener,
-                        // take it off the list.
-                        sListeners.remove(i);
-                        disableSensorLocked(sensor);
-                    }
-                    break;
+        synchronized (mSensorEventQueueMap) {
+
+            // remove this listener/sensor from our list
+            final ArrayList<SensorEventListenerSensorPair> copy =
+                    new ArrayList<SensorEventListenerSensorPair>(mListenerDelegates);
+            int lastIndex = copy.size()-1;
+            for (int i=lastIndex ; i>= 0 ; i--) {
+                if (copy.get(i).isSameListenerSensorPair(listener, sensor)) {
+                    mListenerDelegates.remove(i);
+                }
+            }
+
+            // find the SensorEventQueue associated to this SensorEventListener
+            SensorEventQueue queue = mSensorEventQueueMap.get(listener);
+            if (queue != null) {
+                if (sensor != null) {
+                    queue.removeSensor(sensor);
+                } else {
+                    queue.removeAllSensors();
+                }
+                if (!queue.hasSensors()) {
+                    mSensorEventQueueMap.remove(listener);
+                    queue.dispose();
                 }
             }
         }
     }
 
-    private static native void nativeClassInit();
 
-    private static native int sensors_module_init();
-    private static native int sensors_module_get_next_sensor(Sensor sensor, int next);
+    /*
+     * ListenerDelegate is essentially a SensorEventListener, Sensor pair
+     * and is associated with a single SensorEventQueue.
+     */
+    private static final class SensorEventListenerSensorPair {
+        private final SensorEventListener mSensorEventListener;
+        private final Sensor mSensor;
+        public SensorEventListenerSensorPair(SensorEventListener listener, Sensor sensor) {
+            mSensorEventListener = listener;
+            mSensor = sensor;
+        }
+        public boolean isSameListenerSensorPair(SensorEventListener listener, Sensor sensor) {
+            // if sensor is null, we match only on the listener
+            if (sensor != null) {
+                return (listener == mSensorEventListener) &&
+                        (sensor.getHandle() == mSensor.getHandle());
+            } else {
+                return (listener == mSensorEventListener);
+            }
+        }
+    }
 
-    // Used within this module from outside SensorManager, don't make private
-    static native int sensors_create_queue();
-    static native void sensors_destroy_queue(int queue);
-    static native boolean sensors_enable_sensor(int queue, String name, int sensor, int enable);
-    static native int sensors_data_poll(int queue, float[] values, int[] status, long[] timestamp);
+    /*
+     * SensorEventQueue is the communication channel with the sensor service,
+     * there is a one-to-one mapping between SensorEventQueue and
+     * SensorEventListener.
+     */
+    private static final class SensorEventQueue {
+        private static native int nativeInitSensorEventQueue(SensorEventQueue eventQ, MessageQueue msgQ, float[] scratch);
+        private static native int nativeEnableSensor(int eventQ, int handle, int us);
+        private static native int nativeDisableSensor(int eventQ, int handle);
+        private static native void nativeDestroySensorEventQueue(int eventQ);
+        private int nSensorEventQueue;
+        private final SensorEventListener mListener;
+        private final SparseBooleanArray mActiveSensors = new SparseBooleanArray();
+        private final SparseIntArray mSensorAccuracies = new SparseIntArray();
+        private final SparseBooleanArray mFirstEvent = new SparseBooleanArray();
+        private final CloseGuard mCloseGuard = CloseGuard.get();
+        private final float[] mScratch = new float[16];
+
+        public SensorEventQueue(SensorEventListener listener, MessageQueue msgQ) {
+            nSensorEventQueue = nativeInitSensorEventQueue(this, msgQ, mScratch);
+            mListener = listener;
+            mCloseGuard.open("dispose");
+        }
+        public void dispose() {
+            dispose(false);
+        }
+
+        public boolean addSensor(Sensor sensor, int delay) {
+            if (enableSensor(sensor, delay) == 0) {
+                mActiveSensors.put(sensor.getHandle(), true);
+                return true;
+            }
+            return false;
+        }
+
+        public void removeAllSensors() {
+            for (int i=0 ; i<mActiveSensors.size(); i++) {
+                if (mActiveSensors.valueAt(i) == true) {
+                    int handle = mActiveSensors.keyAt(i);
+                    Sensor sensor = sHandleToSensor.get(handle);
+                    if (sensor != null) {
+                        disableSensor(sensor);
+                        mActiveSensors.put(handle, false);
+                    } else {
+                        // it should never happen -- just ignore.
+                    }
+                }
+            }
+        }
+
+        public void removeSensor(Sensor sensor) {
+            final int handle = sensor.getHandle();
+            if (mActiveSensors.get(handle)) {
+                disableSensor(sensor);
+                mActiveSensors.put(sensor.getHandle(), false);
+            }
+        }
+
+        public boolean hasSensors() {
+            // no more sensors are set
+            return mActiveSensors.indexOfValue(true) >= 0;
+        }
+        
+        @Override
+        protected void finalize() throws Throwable {
+            try {
+                dispose(true);
+            } finally {
+                super.finalize();
+            }
+        }
+
+        private void dispose(boolean finalized) {
+            if (mCloseGuard != null) {
+                if (finalized) {
+                    mCloseGuard.warnIfOpen();
+                }
+                mCloseGuard.close();
+            }
+            if (nSensorEventQueue != 0) {
+                nativeDestroySensorEventQueue(nSensorEventQueue);
+                nSensorEventQueue = 0;
+            }
+        }
+
+        private int enableSensor(Sensor sensor, int us) {
+            if (nSensorEventQueue == 0) throw new NullPointerException();
+            if (sensor == null) throw new NullPointerException();
+            return nativeEnableSensor(nSensorEventQueue, sensor.getHandle(), us);
+        }
+        private int disableSensor(Sensor sensor) {
+            if (nSensorEventQueue == 0) throw new NullPointerException();
+            if (sensor == null) throw new NullPointerException();
+            return nativeDisableSensor(nSensorEventQueue, sensor.getHandle());
+        }
+
+        // Called from native code.
+        @SuppressWarnings("unused")
+        private void dispatchSensorEvent(int handle, float[] values, int inAccuracy, long timestamp) {
+            // this is always called on the same thread.
+            final SensorEvent t = sPool.getFromPool();
+            try {
+                final Sensor sensor = sHandleToSensor.get(handle);
+                final SensorEventListener listener = mListener;
+                // FIXME: handle more than 3 values
+                System.arraycopy(values, 0, t.values, 0, 3);
+                t.timestamp = timestamp;
+                t.accuracy = inAccuracy;
+                t.sensor = sensor;
+                switch (t.sensor.getType()) {
+                    // Only report accuracy for sensors that support it.
+                    case Sensor.TYPE_MAGNETIC_FIELD:
+                    case Sensor.TYPE_ORIENTATION:
+                        // call onAccuracyChanged() only if the value changes
+                        final int accuracy = mSensorAccuracies.get(handle);
+                        if ((t.accuracy >= 0) && (accuracy != t.accuracy)) {
+                            mSensorAccuracies.put(handle, t.accuracy);
+                            listener.onAccuracyChanged(t.sensor, t.accuracy);
+                        }
+                        break;
+                    default:
+                        // For other sensors, just report the accuracy once
+                        if (mFirstEvent.get(handle) == false) {
+                            mFirstEvent.put(handle, true);
+                            listener.onAccuracyChanged(
+                                    t.sensor, SENSOR_STATUS_ACCURACY_HIGH);
+                        }
+                        break;
+                }
+                listener.onSensorChanged(t);
+            } finally {
+                sPool.returnToPool(t);
+            }
+        }
+    }
+
+    /*
+     * A dumb pool of SensorEvent
+     */
+    private static final class SensorEventPool {
+        private final int mPoolSize;
+        private final SensorEvent mPool[];
+        private int mNumItemsInPool;
+
+        private SensorEvent createSensorEvent() {
+            // maximal size for all legacy events is 3
+            return new SensorEvent(3);
+        }
+
+        SensorEventPool(int poolSize) {
+            mPoolSize = poolSize;
+            mNumItemsInPool = poolSize;
+            mPool = new SensorEvent[poolSize];
+        }
+
+        SensorEvent getFromPool() {
+            SensorEvent t = null;
+            synchronized (this) {
+                if (mNumItemsInPool > 0) {
+                    // remove the "top" item from the pool
+                    final int index = mPoolSize - mNumItemsInPool;
+                    t = mPool[index];
+                    mPool[index] = null;
+                    mNumItemsInPool--;
+                }
+            }
+            if (t == null) {
+                // the pool was empty or this item was removed from the pool for
+                // the first time. In any case, we need to create a new item.
+                t = createSensorEvent();
+            }
+            return t;
+        }
+
+        void returnToPool(SensorEvent t) {
+            synchronized (this) {
+                // is there space left in the pool?
+                if (mNumItemsInPool < mPoolSize) {
+                    // if so, return the item to the pool
+                    mNumItemsInPool++;
+                    final int index = mPoolSize - mNumItemsInPool;
+                    mPool[index] = t;
+                }
+            }
+        }
+    }
 }
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index e437d2e..a014cec 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -315,6 +315,30 @@
         return total;
     }
 
+    /** {@hide} */
+    public static long getMobileTcpRxPackets() {
+        long total = 0;
+        for (String iface : getMobileIfaces()) {
+            final long stat = nativeGetIfaceStat(iface, TYPE_TCP_RX_PACKETS);
+            if (stat != UNSUPPORTED) {
+                total += stat;
+            }
+        }
+        return total;
+    }
+
+    /** {@hide} */
+    public static long getMobileTcpTxPackets() {
+        long total = 0;
+        for (String iface : getMobileIfaces()) {
+            final long stat = nativeGetIfaceStat(iface, TYPE_TCP_TX_PACKETS);
+            if (stat != UNSUPPORTED) {
+                total += stat;
+            }
+        }
+        return total;
+    }
+
     /**
      * Get the total number of packets transmitted through the specified interface.
      *
@@ -587,6 +611,8 @@
     private static final int TYPE_RX_PACKETS = 1;
     private static final int TYPE_TX_BYTES = 2;
     private static final int TYPE_TX_PACKETS = 3;
+    private static final int TYPE_TCP_RX_PACKETS = 4;
+    private static final int TYPE_TCP_TX_PACKETS = 5;
 
     private static native long nativeGetTotalStat(int type);
     private static native long nativeGetIfaceStat(String iface, int type);
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 8051923..80c9324 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -515,22 +515,22 @@
 
     @Override
     public boolean quickReject(float left, float top, float right, float bottom, EdgeType type) {
-        return nQuickReject(mRenderer, left, top, right, bottom, type.nativeInt);
+        return nQuickReject(mRenderer, left, top, right, bottom);
     }
     
     private static native boolean nQuickReject(int renderer, float left, float top,
-            float right, float bottom, int edge);
+            float right, float bottom);
 
     @Override
     public boolean quickReject(Path path, EdgeType type) {
         path.computeBounds(mPathBounds, true);
         return nQuickReject(mRenderer, mPathBounds.left, mPathBounds.top,
-                mPathBounds.right, mPathBounds.bottom, type.nativeInt);
+                mPathBounds.right, mPathBounds.bottom);
     }
 
     @Override
     public boolean quickReject(RectF rect, EdgeType type) {
-        return nQuickReject(mRenderer, rect.left, rect.top, rect.right, rect.bottom, type.nativeInt);
+        return nQuickReject(mRenderer, rect.left, rect.top, rect.right, rect.bottom);
     }
 
     ///////////////////////////////////////////////////////////////////////////
diff --git a/core/java/android/view/SimulatedDpad.java b/core/java/android/view/SimulatedDpad.java
index 1ee416c..883fd49 100644
--- a/core/java/android/view/SimulatedDpad.java
+++ b/core/java/android/view/SimulatedDpad.java
@@ -145,6 +145,10 @@
         if (!synthesizeNewKeys) {
             mHandler.removeMessages(MSG_FLICK);
         }
+        InputDevice device = event.getDevice();
+        if (device == null) {
+            return;
+        }
         // Store what time the touchpad event occurred
         final long time = SystemClock.uptimeMillis();
         switch (event.getAction()) {
@@ -157,7 +161,7 @@
                 mAccumulatedY = 0;
                 mLastMoveX = 0;
                 mLastMoveY = 0;
-                if (event.getDevice().getMotionRange(MotionEvent.AXIS_Y).getMax()
+                if (device.getMotionRange(MotionEvent.AXIS_Y).getMax()
                         * EDGE_SWIPE_THRESHOLD < event.getY()) {
                     // Did the swipe begin in a valid region
                     mEdgeSwipePossible = true;
@@ -176,7 +180,7 @@
                 }
                 // Checks if the swipe has crossed the midpoint
                 // and if our swipe gesture is complete
-                if (event.getY() < (event.getDevice().getMotionRange(MotionEvent.AXIS_Y).getMax()
+                if (event.getY() < (device.getMotionRange(MotionEvent.AXIS_Y).getMax()
                         * .5) && mEdgeSwipePossible) {
                     mEdgeSwipePossible = false;
 
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index a972b75..c10f287 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -215,7 +215,6 @@
     private int mNativeSurfaceControl; // SurfaceControl*
     private int mGenerationId; // incremented each time mNativeSurface changes
     private final Canvas mCanvas = new CompatibleCanvas();
-    private int mCanvasSaveCount; // Canvas save count at time of lockCanvas()
 
     // The Translator for density compatibility mode.  This is used for scaling
     // the canvas to perform the appropriate density transformation.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index cbd0668..a1305e4 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4859,13 +4859,25 @@
         event.setEnabled(isEnabled());
         event.setContentDescription(mContentDescription);
 
-        if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED && mAttachInfo != null) {
-            ArrayList<View> focusablesTempList = mAttachInfo.mTempArrayList;
-            getRootView().addFocusables(focusablesTempList, View.FOCUS_FORWARD,
-                    FOCUSABLES_ALL);
-            event.setItemCount(focusablesTempList.size());
-            event.setCurrentItemIndex(focusablesTempList.indexOf(this));
-            focusablesTempList.clear();
+        switch (event.getEventType()) {
+            case AccessibilityEvent.TYPE_VIEW_FOCUSED: {
+                ArrayList<View> focusablesTempList = (mAttachInfo != null)
+                        ? mAttachInfo.mTempArrayList : new ArrayList<View>();
+                getRootView().addFocusables(focusablesTempList, View.FOCUS_FORWARD, FOCUSABLES_ALL);
+                event.setItemCount(focusablesTempList.size());
+                event.setCurrentItemIndex(focusablesTempList.indexOf(this));
+                if (mAttachInfo != null) {
+                    focusablesTempList.clear();
+                }
+            } break;
+            case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED: {
+                CharSequence text = getIterableTextForAccessibility();
+                if (text != null && text.length() > 0) {
+                    event.setFromIndex(getAccessibilitySelectionStart());
+                    event.setToIndex(getAccessibilitySelectionEnd());
+                    event.setItemCount(text.length());
+                }
+            } break;
         }
     }
 
@@ -5081,7 +5093,10 @@
             info.addAction(AccessibilityNodeInfo.ACTION_LONG_CLICK);
         }
 
-        if (mContentDescription != null && mContentDescription.length() > 0) {
+        CharSequence text = getIterableTextForAccessibility();
+        if (text != null && text.length() > 0) {
+            info.setTextSelection(getAccessibilitySelectionStart(), getAccessibilitySelectionEnd());
+
             info.addAction(AccessibilityNodeInfo.ACTION_SET_SELECTION);
             info.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY);
             info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY);
@@ -7153,11 +7168,15 @@
      * @hide
      */
     public void setAccessibilitySelection(int start, int end) {
+        if (start ==  end && end == mAccessibilityCursorPosition) {
+            return;
+        }
         if (start >= 0 && start == end && end <= getIterableTextForAccessibility().length()) {
             mAccessibilityCursorPosition = start;
         } else {
             mAccessibilityCursorPosition = ACCESSIBILITY_CURSOR_POSITION_UNDEFINED;
         }
+        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED);
     }
 
     private void sendViewTextTraversedAtGranularityEvent(int action, int granularity,
@@ -8166,8 +8185,7 @@
             // in onHoverEvent.
             // Note that onGenericMotionEvent will be called by default when
             // onHoverEvent returns false (refer to dispatchGenericMotionEvent).
-            dispatchGenericMotionEventInternal(event);
-            return true;
+            return dispatchGenericMotionEventInternal(event);
         }
 
         return false;
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index f61c879..ad87fcb 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -372,29 +372,31 @@
 
     // Boolean attributes.
 
-    private static final int PROPERTY_CHECKABLE = 0x00000001;
+    private static final int BOOLEAN_PROPERTY_CHECKABLE = 0x00000001;
 
-    private static final int PROPERTY_CHECKED = 0x00000002;
+    private static final int BOOLEAN_PROPERTY_CHECKED = 0x00000002;
 
-    private static final int PROPERTY_FOCUSABLE = 0x00000004;
+    private static final int BOOLEAN_PROPERTY_FOCUSABLE = 0x00000004;
 
-    private static final int PROPERTY_FOCUSED = 0x00000008;
+    private static final int BOOLEAN_PROPERTY_FOCUSED = 0x00000008;
 
-    private static final int PROPERTY_SELECTED = 0x00000010;
+    private static final int BOOLEAN_PROPERTY_SELECTED = 0x00000010;
 
-    private static final int PROPERTY_CLICKABLE = 0x00000020;
+    private static final int BOOLEAN_PROPERTY_CLICKABLE = 0x00000020;
 
-    private static final int PROPERTY_LONG_CLICKABLE = 0x00000040;
+    private static final int BOOLEAN_PROPERTY_LONG_CLICKABLE = 0x00000040;
 
-    private static final int PROPERTY_ENABLED = 0x00000080;
+    private static final int BOOLEAN_PROPERTY_ENABLED = 0x00000080;
 
-    private static final int PROPERTY_PASSWORD = 0x00000100;
+    private static final int BOOLEAN_PROPERTY_PASSWORD = 0x00000100;
 
-    private static final int PROPERTY_SCROLLABLE = 0x00000200;
+    private static final int BOOLEAN_PROPERTY_SCROLLABLE = 0x00000200;
 
-    private static final int PROPERTY_ACCESSIBILITY_FOCUSED = 0x00000400;
+    private static final int BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED = 0x00000400;
 
-    private static final int PROPERTY_VISIBLE_TO_USER = 0x00000800;
+    private static final int BOOLEAN_PROPERTY_VISIBLE_TO_USER = 0x00000800;
+
+    private static final int BOOLEAN_PROPERTY_EDITABLE = 0x00001000;
 
     /**
      * Bits that provide the id of a virtual descendant of a view.
@@ -478,6 +480,9 @@
 
     private int mMovementGranularities;
 
+    private int mTextSelectionStart = UNDEFINED;
+    private int mTextSelectionEnd = UNDEFINED;
+
     private int mConnectionId = UNDEFINED;
 
     /**
@@ -987,7 +992,7 @@
      * @return True if the node is checkable.
      */
     public boolean isCheckable() {
-        return getBooleanProperty(PROPERTY_CHECKABLE);
+        return getBooleanProperty(BOOLEAN_PROPERTY_CHECKABLE);
     }
 
     /**
@@ -1003,7 +1008,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setCheckable(boolean checkable) {
-        setBooleanProperty(PROPERTY_CHECKABLE, checkable);
+        setBooleanProperty(BOOLEAN_PROPERTY_CHECKABLE, checkable);
     }
 
     /**
@@ -1012,7 +1017,7 @@
      * @return True if the node is checked.
      */
     public boolean isChecked() {
-        return getBooleanProperty(PROPERTY_CHECKED);
+        return getBooleanProperty(BOOLEAN_PROPERTY_CHECKED);
     }
 
     /**
@@ -1028,7 +1033,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setChecked(boolean checked) {
-        setBooleanProperty(PROPERTY_CHECKED, checked);
+        setBooleanProperty(BOOLEAN_PROPERTY_CHECKED, checked);
     }
 
     /**
@@ -1037,7 +1042,7 @@
      * @return True if the node is focusable.
      */
     public boolean isFocusable() {
-        return getBooleanProperty(PROPERTY_FOCUSABLE);
+        return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSABLE);
     }
 
     /**
@@ -1053,7 +1058,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setFocusable(boolean focusable) {
-        setBooleanProperty(PROPERTY_FOCUSABLE, focusable);
+        setBooleanProperty(BOOLEAN_PROPERTY_FOCUSABLE, focusable);
     }
 
     /**
@@ -1062,7 +1067,7 @@
      * @return True if the node is focused.
      */
     public boolean isFocused() {
-        return getBooleanProperty(PROPERTY_FOCUSED);
+        return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED);
     }
 
     /**
@@ -1078,7 +1083,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setFocused(boolean focused) {
-        setBooleanProperty(PROPERTY_FOCUSED, focused);
+        setBooleanProperty(BOOLEAN_PROPERTY_FOCUSED, focused);
     }
 
     /**
@@ -1087,7 +1092,7 @@
      * @return Whether the node is visible to the user.
      */
     public boolean isVisibleToUser() {
-        return getBooleanProperty(PROPERTY_VISIBLE_TO_USER);
+        return getBooleanProperty(BOOLEAN_PROPERTY_VISIBLE_TO_USER);
     }
 
     /**
@@ -1103,7 +1108,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setVisibleToUser(boolean visibleToUser) {
-        setBooleanProperty(PROPERTY_VISIBLE_TO_USER, visibleToUser);
+        setBooleanProperty(BOOLEAN_PROPERTY_VISIBLE_TO_USER, visibleToUser);
     }
 
     /**
@@ -1112,7 +1117,7 @@
      * @return True if the node is accessibility focused.
      */
     public boolean isAccessibilityFocused() {
-        return getBooleanProperty(PROPERTY_ACCESSIBILITY_FOCUSED);
+        return getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED);
     }
 
     /**
@@ -1128,7 +1133,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setAccessibilityFocused(boolean focused) {
-        setBooleanProperty(PROPERTY_ACCESSIBILITY_FOCUSED, focused);
+        setBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED, focused);
     }
 
     /**
@@ -1137,7 +1142,7 @@
      * @return True if the node is selected.
      */
     public boolean isSelected() {
-        return getBooleanProperty(PROPERTY_SELECTED);
+        return getBooleanProperty(BOOLEAN_PROPERTY_SELECTED);
     }
 
     /**
@@ -1153,7 +1158,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setSelected(boolean selected) {
-        setBooleanProperty(PROPERTY_SELECTED, selected);
+        setBooleanProperty(BOOLEAN_PROPERTY_SELECTED, selected);
     }
 
     /**
@@ -1162,7 +1167,7 @@
      * @return True if the node is clickable.
      */
     public boolean isClickable() {
-        return getBooleanProperty(PROPERTY_CLICKABLE);
+        return getBooleanProperty(BOOLEAN_PROPERTY_CLICKABLE);
     }
 
     /**
@@ -1178,7 +1183,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setClickable(boolean clickable) {
-        setBooleanProperty(PROPERTY_CLICKABLE, clickable);
+        setBooleanProperty(BOOLEAN_PROPERTY_CLICKABLE, clickable);
     }
 
     /**
@@ -1187,7 +1192,7 @@
      * @return True if the node is long clickable.
      */
     public boolean isLongClickable() {
-        return getBooleanProperty(PROPERTY_LONG_CLICKABLE);
+        return getBooleanProperty(BOOLEAN_PROPERTY_LONG_CLICKABLE);
     }
 
     /**
@@ -1203,7 +1208,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setLongClickable(boolean longClickable) {
-        setBooleanProperty(PROPERTY_LONG_CLICKABLE, longClickable);
+        setBooleanProperty(BOOLEAN_PROPERTY_LONG_CLICKABLE, longClickable);
     }
 
     /**
@@ -1212,7 +1217,7 @@
      * @return True if the node is enabled.
      */
     public boolean isEnabled() {
-        return getBooleanProperty(PROPERTY_ENABLED);
+        return getBooleanProperty(BOOLEAN_PROPERTY_ENABLED);
     }
 
     /**
@@ -1228,7 +1233,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setEnabled(boolean enabled) {
-        setBooleanProperty(PROPERTY_ENABLED, enabled);
+        setBooleanProperty(BOOLEAN_PROPERTY_ENABLED, enabled);
     }
 
     /**
@@ -1237,7 +1242,7 @@
      * @return True if the node is a password.
      */
     public boolean isPassword() {
-        return getBooleanProperty(PROPERTY_PASSWORD);
+        return getBooleanProperty(BOOLEAN_PROPERTY_PASSWORD);
     }
 
     /**
@@ -1253,7 +1258,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setPassword(boolean password) {
-        setBooleanProperty(PROPERTY_PASSWORD, password);
+        setBooleanProperty(BOOLEAN_PROPERTY_PASSWORD, password);
     }
 
     /**
@@ -1262,7 +1267,7 @@
      * @return True if the node is scrollable, false otherwise.
      */
     public boolean isScrollable() {
-        return getBooleanProperty(PROPERTY_SCROLLABLE);
+        return getBooleanProperty(BOOLEAN_PROPERTY_SCROLLABLE);
     }
 
     /**
@@ -1279,7 +1284,32 @@
      */
     public void setScrollable(boolean scrollable) {
         enforceNotSealed();
-        setBooleanProperty(PROPERTY_SCROLLABLE, scrollable);
+        setBooleanProperty(BOOLEAN_PROPERTY_SCROLLABLE, scrollable);
+    }
+
+    /**
+     * Gets if the node is editable.
+     *
+     * @return True if the node is editable, false otherwise.
+     */
+    public boolean isEditable() {
+        return getBooleanProperty(BOOLEAN_PROPERTY_EDITABLE);
+    }
+
+    /**
+     * Sets whether this node is editable.
+     * <p>
+     *   <strong>Note:</strong> Cannot be called from an
+     *   {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     *
+     * @param editable True if the node is editable.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setEditable(boolean editable) {
+        setBooleanProperty(BOOLEAN_PROPERTY_EDITABLE, editable);
     }
 
     /**
@@ -1533,6 +1563,43 @@
     }
 
     /**
+     * Gets the text selection start.
+     *
+     * @return The text selection start if there is selection or -1.
+     */
+    public int getTextSelectionStart() {
+        return mTextSelectionStart;
+    }
+
+    /**
+     * Gets the text selection end.
+     *
+     * @return The text selection end if there is selection or -1.
+     */
+    public int getTextSelectionEnd() {
+        return mTextSelectionEnd;
+    }
+
+    /**
+     * Sets the text selection start and end.
+     * <p>
+     *   <strong>Note:</strong> Cannot be called from an
+     *   {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     *
+     * @param start The text selection start.
+     * @param end The text selection end.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setTextSelection(int start, int end) {
+        enforceNotSealed();
+        mTextSelectionStart = start;
+        mTextSelectionEnd = end;
+    }
+
+    /**
      * Gets the value of a boolean property.
      *
      * @param property The property.
@@ -1776,6 +1843,9 @@
         parcel.writeCharSequence(mContentDescription);
         parcel.writeCharSequence(mViewIdResourceName);
 
+        parcel.writeInt(mTextSelectionStart);
+        parcel.writeInt(mTextSelectionEnd);
+
         // Since instances of this class are fetched via synchronous i.e. blocking
         // calls in IPCs we always recycle as soon as the instance is marshaled.
         recycle();
@@ -1808,6 +1878,8 @@
         for (int i = 0; i < otherChildIdCount; i++) {
             mChildNodeIds.put(i, other.mChildNodeIds.valueAt(i));    
         }
+        mTextSelectionStart = other.mTextSelectionStart;
+        mTextSelectionEnd = other.mTextSelectionEnd;
     }
 
     /**
@@ -1852,6 +1924,9 @@
         mText = parcel.readCharSequence();
         mContentDescription = parcel.readCharSequence();
         mViewIdResourceName = parcel.readCharSequence();
+
+        mTextSelectionStart = parcel.readInt();
+        mTextSelectionEnd = parcel.readInt();
     }
 
     /**
@@ -1876,6 +1951,8 @@
         mContentDescription = null;
         mViewIdResourceName = null;
         mActions = 0;
+        mTextSelectionStart = UNDEFINED;
+        mTextSelectionEnd = UNDEFINED;
     }
 
     /**
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 74ded18..2ac5a12 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -1969,8 +1969,10 @@
                  * Ensure the user can't type in a value greater than the max
                  * allowed. We have to allow less than min as the user might
                  * want to delete some numbers and then type a new number.
+                 * And prevent multiple-"0" that exceeds the length of upper
+                 * bound number.
                  */
-                if (val > mMaxValue) {
+                if (val > mMaxValue || result.length() > String.valueOf(mMaxValue).length()) {
                     return "";
                 } else {
                     return filtered;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 1579e79..2145419 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -7165,6 +7165,7 @@
      */
     protected void onSelectionChanged(int selStart, int selEnd) {
         sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED);
+        notifyAccessibilityStateChanged();
     }
 
     /**
@@ -7976,6 +7977,10 @@
             info.setText(getTextForAccessibility());
         }
 
+        if (mBufferType == BufferType.EDITABLE) {
+            info.setEditable(true);
+        }
+
         if (TextUtils.isEmpty(getContentDescription()) && !TextUtils.isEmpty(mText)) {
             info.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY);
             info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY);
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index d705024..44e8757 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -160,7 +160,8 @@
 	external/skia/include/effects \
 	external/skia/include/images \
 	external/skia/include/ports \
-	external/skia/src/ports \
+	external/skia/src/core \
+	external/skia/src/images \
 	external/skia/include/utils \
 	external/sqlite/dist \
 	external/sqlite/android \
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index b7fdecf..daabce3 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -231,7 +231,7 @@
     }
 
     SkAutoTDelete<SkImageDecoder> add(decoder);
-    SkAutoTDelete<SkBitmap> adb(bitmap, !useExistingBitmap);
+    SkAutoTDelete<SkBitmap> adb(!useExistingBitmap ? bitmap : NULL);
 
     decoder->setPeeker(&peeker);
     if (!isPurgeable) {
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index 5d6f738..7208c57 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -93,14 +93,6 @@
         return canvas->getDevice()->accessBitmap(false).height();
     }
 
-    static void setBitmap(JNIEnv* env, jobject, SkCanvas* canvas, SkBitmap* bitmap) {
-        if (bitmap) {
-            canvas->setBitmapDevice(*bitmap);
-        } else {
-            canvas->setDevice(NULL);
-        }
-    }
- 
     static int saveAll(JNIEnv* env, jobject jcanvas) {
         NPE_CHECK_RETURN_ZERO(env, jcanvas);
         return GraphicsJNI::getNativeCanvas(env, jcanvas)->save();
@@ -278,25 +270,25 @@
         canvas->setDrawFilter(filter);
     }
     
-    static jboolean quickReject__RectFI(JNIEnv* env, jobject, SkCanvas* canvas,
-                                        jobject rect, int edgetype) {
+    static jboolean quickReject__RectF(JNIEnv* env, jobject, SkCanvas* canvas,
+                                        jobject rect) {
         SkRect rect_;
         GraphicsJNI::jrectf_to_rect(env, rect, &rect_);
-        return canvas->quickReject(rect_, (SkCanvas::EdgeType)edgetype);
+        return canvas->quickReject(rect_);
     }
- 
-    static jboolean quickReject__PathI(JNIEnv* env, jobject, SkCanvas* canvas,
-                                       SkPath* path, int edgetype) {
-        return canvas->quickReject(*path, (SkCanvas::EdgeType)edgetype);
+
+    static jboolean quickReject__Path(JNIEnv* env, jobject, SkCanvas* canvas,
+                                       SkPath* path) {
+        return canvas->quickReject(*path);
     }
- 
-    static jboolean quickReject__FFFFI(JNIEnv* env, jobject, SkCanvas* canvas,
+
+    static jboolean quickReject__FFFF(JNIEnv* env, jobject, SkCanvas* canvas,
                                        jfloat left, jfloat top, jfloat right,
-                                       jfloat bottom, int edgetype) {
+                                       jfloat bottom) {
         SkRect r;
         r.set(SkFloatToScalar(left), SkFloatToScalar(top),
               SkFloatToScalar(right), SkFloatToScalar(bottom));
-        return canvas->quickReject(r, (SkCanvas::EdgeType)edgetype);
+        return canvas->quickReject(r);
     }
  
     static void drawRGB(JNIEnv* env, jobject, SkCanvas* canvas,
@@ -938,12 +930,19 @@
                               jobject bounds) {
         SkRect   r;
         SkIRect ir;
-        bool     result = canvas->getClipBounds(&r, SkCanvas::kBW_EdgeType);
+        bool     result = canvas->getClipBounds(&r);
 
         if (!result) {
             r.setEmpty();
+        } else {
+            // ensure the clip is not larger than the canvas
+            SkRect canvasRect;
+            SkISize deviceSize = canvas->getDeviceSize();
+            canvasRect.iset(0, 0, deviceSize.fWidth, deviceSize.fHeight);
+            r.intersect(canvasRect);
         }
         r.round(&ir);
+
         (void)GraphicsJNI::irect_to_jrect(ir, env, bounds);
         return result;
     }
@@ -960,7 +959,6 @@
     {"isOpaque","()Z", (void*) SkCanvasGlue::isOpaque},
     {"getWidth","()I", (void*) SkCanvasGlue::getWidth},
     {"getHeight","()I", (void*) SkCanvasGlue::getHeight},
-    {"native_setBitmap","(II)V", (void*) SkCanvasGlue::setBitmap},
     {"save","()I", (void*) SkCanvasGlue::saveAll},
     {"save","(I)I", (void*) SkCanvasGlue::save},
     {"native_saveLayer","(ILandroid/graphics/RectF;II)I",
@@ -992,10 +990,10 @@
     {"native_getClipBounds","(ILandroid/graphics/Rect;)Z",
         (void*) SkCanvasGlue::getClipBounds},
     {"native_getCTM", "(II)V", (void*)SkCanvasGlue::getCTM},
-    {"native_quickReject","(ILandroid/graphics/RectF;I)Z",
-        (void*) SkCanvasGlue::quickReject__RectFI},
-    {"native_quickReject","(III)Z", (void*) SkCanvasGlue::quickReject__PathI},
-    {"native_quickReject","(IFFFFI)Z", (void*)SkCanvasGlue::quickReject__FFFFI},
+    {"native_quickReject","(ILandroid/graphics/RectF;)Z",
+        (void*) SkCanvasGlue::quickReject__RectF},
+    {"native_quickReject","(II)Z", (void*) SkCanvasGlue::quickReject__Path},
+    {"native_quickReject","(IFFFF)Z", (void*)SkCanvasGlue::quickReject__FFFF},
     {"native_drawRGB","(IIII)V", (void*) SkCanvasGlue::drawRGB},
     {"native_drawARGB","(IIIII)V", (void*) SkCanvasGlue::drawARGB},
     {"native_drawColor","(II)V", (void*) SkCanvasGlue::drawColor__I},
diff --git a/core/jni/android/graphics/NinePatchImpl.cpp b/core/jni/android/graphics/NinePatchImpl.cpp
index ff0eb45..01e7e3e 100644
--- a/core/jni/android/graphics/NinePatchImpl.cpp
+++ b/core/jni/android/graphics/NinePatchImpl.cpp
@@ -105,7 +105,7 @@
 void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds,
                        const SkBitmap& bitmap, const android::Res_png_9patch& chunk,
                        const SkPaint* paint, SkRegion** outRegion) {
-    if (canvas && canvas->quickReject(bounds, SkCanvas::kBW_EdgeType)) {
+    if (canvas && canvas->quickReject(bounds)) {
         return;
     }
 
diff --git a/core/jni/android/graphics/Region.cpp b/core/jni/android/graphics/Region.cpp
index ab7cf46..ded2186 100644
--- a/core/jni/android/graphics/Region.cpp
+++ b/core/jni/android/graphics/Region.cpp
@@ -177,7 +177,7 @@
 
     SkRegion* region = new SkRegion;
     size_t size = p->readInt32();
-    region->unflatten(p->readInplace(size));
+    region->readFromMemory(p->readInplace(size));
 
     return region;
 }
@@ -190,9 +190,9 @@
 
     android::Parcel* p = android::parcelForJavaObject(env, parcel);
 
-    size_t size = region->flatten(NULL);
+    size_t size = region->writeToMemory(NULL);
     p->writeInt32(size);
-    region->flatten(p->writeInplace(size));
+    region->writeToMemory(p->writeInplace(size));
 
     return true;
 }
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index 6d3c878..7544645 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -345,7 +345,7 @@
 }
 
 void TextLayoutShaper::init() {
-    mDefaultTypeface = SkFontHost::CreateTypeface(NULL, NULL, NULL, 0, SkTypeface::kNormal);
+    mDefaultTypeface = SkFontHost::CreateTypeface(NULL, NULL, SkTypeface::kNormal);
 }
 
 void TextLayoutShaper::unrefTypefaces() {
diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp
index 7f4c37b..e056b61 100644
--- a/core/jni/android/graphics/Typeface.cpp
+++ b/core/jni/android/graphics/Typeface.cpp
@@ -147,25 +147,6 @@
     return SkTypeface::CreateFromFile(str.c_str());
 }
 
-#define MIN_GAMMA   (0.1f)
-#define MAX_GAMMA   (10.0f)
-static float pinGamma(float gamma) {
-    if (gamma < MIN_GAMMA) {
-        gamma = MIN_GAMMA;
-    } else if (gamma > MAX_GAMMA) {
-        gamma = MAX_GAMMA;
-    }
-    return gamma;
-}
-
-extern void skia_set_text_gamma(float, float);
-
-static void Typeface_setGammaForText(JNIEnv* env, jobject, jfloat blackGamma,
-                                     jfloat whiteGamma) {
-    // Comment this out for release builds. This is only used during development
-    skia_set_text_gamma(pinGamma(blackGamma), pinGamma(whiteGamma));
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 
 static JNINativeMethod gTypefaceMethods[] = {
@@ -177,7 +158,6 @@
                                            (void*)Typeface_createFromAsset },
     { "nativeCreateFromFile",     "(Ljava/lang/String;)I",
                                            (void*)Typeface_createFromFile },
-    { "setGammaForText", "(FF)V", (void*)Typeface_setGammaForText },
 };
 
 int register_android_graphics_Typeface(JNIEnv* env)
diff --git a/core/jni/android_database_SQLiteConnection.cpp b/core/jni/android_database_SQLiteConnection.cpp
index c9cf2fa..f70f0d1 100644
--- a/core/jni/android_database_SQLiteConnection.cpp
+++ b/core/jni/android_database_SQLiteConnection.cpp
@@ -706,7 +706,7 @@
             }
 
             CopyRowResult cpr = copyRow(env, window, statement, numColumns, startPos, addedRows);
-            if (cpr == CPR_FULL && addedRows && startPos + addedRows < requiredPos) {
+            if (cpr == CPR_FULL && addedRows && startPos + addedRows <= requiredPos) {
                 // We filled the window before we got to the one row that we really wanted.
                 // Clear the window and start filling it again from here.
                 // TODO: Would be nicer if we could progressively replace earlier rows.
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index 3c1b9c8..e8a6569 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -16,7 +16,8 @@
 
 #define LOG_TAG "SensorManager"
 
-#include "utils/Log.h"
+#include <utils/Log.h>
+#include <utils/Looper.h>
 
 #include <gui/Sensor.h>
 #include <gui/SensorManager.h>
@@ -24,7 +25,13 @@
 
 #include "jni.h"
 #include "JNIHelp.h"
+#include "android_os_MessageQueue.h"
+#include <android_runtime/AndroidRuntime.h>
 
+static struct {
+    jclass clazz;
+    jmethodID dispatchSensorEvent;
+} gSensorEventQueueClassInfo;
 
 namespace android {
 
@@ -41,20 +48,29 @@
     jfieldID    minDelay;
 } gSensorOffsets;
 
+
 /*
  * The method below are not thread-safe and not intended to be
  */
 
-
-static jint
-sensors_module_init(JNIEnv *env, jclass clazz)
+static void
+nativeClassInit (JNIEnv *_env, jclass _this)
 {
-    SensorManager::getInstance();
-    return 0;
+    jclass sensorClass = _env->FindClass("android/hardware/Sensor");
+    SensorOffsets& sensorOffsets = gSensorOffsets;
+    sensorOffsets.name        = _env->GetFieldID(sensorClass, "mName",      "Ljava/lang/String;");
+    sensorOffsets.vendor      = _env->GetFieldID(sensorClass, "mVendor",    "Ljava/lang/String;");
+    sensorOffsets.version     = _env->GetFieldID(sensorClass, "mVersion",   "I");
+    sensorOffsets.handle      = _env->GetFieldID(sensorClass, "mHandle",    "I");
+    sensorOffsets.type        = _env->GetFieldID(sensorClass, "mType",      "I");
+    sensorOffsets.range       = _env->GetFieldID(sensorClass, "mMaxRange",  "F");
+    sensorOffsets.resolution  = _env->GetFieldID(sensorClass, "mResolution","F");
+    sensorOffsets.power       = _env->GetFieldID(sensorClass, "mPower",     "F");
+    sensorOffsets.minDelay    = _env->GetFieldID(sensorClass, "mMinDelay",  "I");
 }
 
 static jint
-sensors_module_get_next_sensor(JNIEnv *env, jobject clazz, jobject sensor, jint next)
+nativeGetNextSensor(JNIEnv *env, jclass clazz, jobject sensor, jint next)
 {
     SensorManager& mgr(SensorManager::getInstance());
 
@@ -82,106 +98,161 @@
 }
 
 //----------------------------------------------------------------------------
-static jint
-sensors_create_queue(JNIEnv *env, jclass clazz)
-{
+
+class Receiver : public LooperCallback {
+    sp<SensorEventQueue> mSensorQueue;
+    sp<MessageQueue> mMessageQueue;
+    jobject mReceiverObject;
+    jfloatArray mScratch;
+public:
+    Receiver(const sp<SensorEventQueue>& sensorQueue,
+            const sp<MessageQueue>& messageQueue,
+            jobject receiverObject, jfloatArray scratch) {
+        JNIEnv* env = AndroidRuntime::getJNIEnv();
+        mSensorQueue = sensorQueue;
+        mMessageQueue = messageQueue;
+        mReceiverObject = env->NewGlobalRef(receiverObject);
+        mScratch = (jfloatArray)env->NewGlobalRef(scratch);
+    }
+    ~Receiver() {
+        JNIEnv* env = AndroidRuntime::getJNIEnv();
+        env->DeleteGlobalRef(mReceiverObject);
+        env->DeleteGlobalRef(mScratch);
+    }
+    sp<SensorEventQueue> getSensorEventQueue() const {
+        return mSensorQueue;
+    }
+
+    void destroy() {
+        mMessageQueue->getLooper()->removeFd( mSensorQueue->getFd() );
+    }
+
+private:
+    virtual void onFirstRef() {
+        LooperCallback::onFirstRef();
+        mMessageQueue->getLooper()->addFd(mSensorQueue->getFd(), 0,
+                ALOOPER_EVENT_INPUT, this, mSensorQueue.get());
+    }
+
+    virtual int handleEvent(int fd, int events, void* data) {
+        JNIEnv* env = AndroidRuntime::getJNIEnv();
+        sp<SensorEventQueue> q = reinterpret_cast<SensorEventQueue *>(data);
+        ssize_t n;
+        ASensorEvent buffer[16];
+        while ((n = q->read(buffer, 16)) > 0) {
+            for (int i=0 ; i<n ; i++) {
+
+                env->SetFloatArrayRegion(mScratch, 0, 16, buffer[i].data);
+
+                env->CallVoidMethod(mReceiverObject,
+                        gSensorEventQueueClassInfo.dispatchSensorEvent,
+                        buffer[i].sensor,
+                        mScratch,
+                        buffer[i].vector.status,
+                        buffer[i].timestamp);
+
+                if (env->ExceptionCheck()) {
+                    ALOGE("Exception dispatching input event.");
+                    return 1;
+                }
+            }
+        }
+        if (n<0 && n != -EAGAIN) {
+            // FIXME: error receiving events, what to do in this case?
+        }
+
+        return 1;
+    }
+};
+
+static jint nativeInitSensorEventQueue(JNIEnv *env, jclass clazz, jobject eventQ, jobject msgQ, jfloatArray scratch) {
     SensorManager& mgr(SensorManager::getInstance());
     sp<SensorEventQueue> queue(mgr.createEventQueue());
-    queue->incStrong(clazz);
-    return reinterpret_cast<int>(queue.get());
-}
 
-static void
-sensors_destroy_queue(JNIEnv *env, jclass clazz, jint nativeQueue)
-{
-    sp<SensorEventQueue> queue(reinterpret_cast<SensorEventQueue *>(nativeQueue));
-    if (queue != 0) {
-        queue->decStrong(clazz);
-    }
-}
-
-static jboolean
-sensors_enable_sensor(JNIEnv *env, jclass clazz,
-        jint nativeQueue, jstring name, jint sensor, jint delay)
-{
-    sp<SensorEventQueue> queue(reinterpret_cast<SensorEventQueue *>(nativeQueue));
-    if (queue == 0) return JNI_FALSE;
-    status_t res;
-    if (delay >= 0) {
-        res = queue->enableSensor(sensor, delay);
-    } else {
-        res = queue->disableSensor(sensor);
-    }
-    return res == NO_ERROR ? true : false;
-}
-
-static jint
-sensors_data_poll(JNIEnv *env, jclass clazz, jint nativeQueue,
-        jfloatArray values, jintArray status, jlongArray timestamp)
-{
-    sp<SensorEventQueue> queue(reinterpret_cast<SensorEventQueue *>(nativeQueue));
-    if (queue == 0) return -1;
-
-    status_t res;
-    ASensorEvent event;
-
-    res = queue->read(&event, 1);
-    if (res == 0) {
-        res = queue->waitForEvent();
-        if (res != NO_ERROR)
-            return -1;
-        // here we're guaranteed to have an event
-        res = queue->read(&event, 1);
-        ALOGE_IF(res==0, "sensors_data_poll: nothing to read after waitForEvent()");
-    }
-    if (res <= 0) {
-        return -1;
+    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, msgQ);
+    if (messageQueue == NULL) {
+        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
+        return 0;
     }
 
-    jint accuracy = event.vector.status;
-    env->SetFloatArrayRegion(values, 0, 3, event.vector.v);
-    env->SetIntArrayRegion(status, 0, 1, &accuracy);
-    env->SetLongArrayRegion(timestamp, 0, 1, &event.timestamp);
-
-    return event.sensor;
+    sp<Receiver> receiver = new Receiver(queue, messageQueue, eventQ, scratch);
+    receiver->incStrong(clazz);
+    return jint(receiver.get());
 }
 
-static void
-nativeClassInit (JNIEnv *_env, jclass _this)
-{
-    jclass sensorClass = _env->FindClass("android/hardware/Sensor");
-    SensorOffsets& sensorOffsets = gSensorOffsets;
-    sensorOffsets.name        = _env->GetFieldID(sensorClass, "mName",      "Ljava/lang/String;");
-    sensorOffsets.vendor      = _env->GetFieldID(sensorClass, "mVendor",    "Ljava/lang/String;");
-    sensorOffsets.version     = _env->GetFieldID(sensorClass, "mVersion",   "I");
-    sensorOffsets.handle      = _env->GetFieldID(sensorClass, "mHandle",    "I");
-    sensorOffsets.type        = _env->GetFieldID(sensorClass, "mType",      "I");
-    sensorOffsets.range       = _env->GetFieldID(sensorClass, "mMaxRange",  "F");
-    sensorOffsets.resolution  = _env->GetFieldID(sensorClass, "mResolution","F");
-    sensorOffsets.power       = _env->GetFieldID(sensorClass, "mPower",     "F");
-    sensorOffsets.minDelay    = _env->GetFieldID(sensorClass, "mMinDelay",  "I");
+static jint nativeEnableSensor(JNIEnv *env, jclass clazz, jint eventQ, jint handle, jint us) {
+    sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ));
+    return receiver->getSensorEventQueue()->enableSensor(handle, us);
 }
 
-static JNINativeMethod gMethods[] = {
-    {"nativeClassInit", "()V",              (void*)nativeClassInit },
-    {"sensors_module_init","()I",           (void*)sensors_module_init },
-    {"sensors_module_get_next_sensor","(Landroid/hardware/Sensor;I)I",
-                                            (void*)sensors_module_get_next_sensor },
+static jint nativeDisableSensor(JNIEnv *env, jclass clazz, jint eventQ, jint handle) {
+    sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ));
+    return receiver->getSensorEventQueue()->disableSensor(handle);
+}
 
-    {"sensors_create_queue",  "()I",        (void*)sensors_create_queue },
-    {"sensors_destroy_queue", "(I)V",       (void*)sensors_destroy_queue },
-    {"sensors_enable_sensor", "(ILjava/lang/String;II)Z",
-                                            (void*)sensors_enable_sensor },
+static void nativeDestroySensorEventQueue(JNIEnv *env, jclass clazz, jint eventQ, jint handle) {
+    sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ));
+    receiver->destroy();
+    receiver->decStrong(clazz);
+}
 
-    {"sensors_data_poll",  "(I[F[I[J)I",     (void*)sensors_data_poll },
+
+//----------------------------------------------------------------------------
+
+static JNINativeMethod gSystemSensorManagerMethods[] = {
+    {"nativeClassInit",
+            "()V",
+            (void*)nativeClassInit },
+
+    {"nativeGetNextSensor",
+            "(Landroid/hardware/Sensor;I)I",
+            (void*)nativeGetNextSensor },
+};
+
+static JNINativeMethod gSensorEventQueueMethods[] = {
+    {"nativeInitSensorEventQueue",
+            "(Landroid/hardware/SystemSensorManager$SensorEventQueue;Landroid/os/MessageQueue;[F)I",
+            (void*)nativeInitSensorEventQueue },
+
+    {"nativeEnableSensor",
+            "(III)I",
+            (void*)nativeEnableSensor },
+
+    {"nativeDisableSensor",
+            "(II)I",
+            (void*)nativeDisableSensor },
+
+    {"nativeDestroySensorEventQueue",
+            "(I)V",
+            (void*)nativeDestroySensorEventQueue },
 };
 
 }; // namespace android
 
 using namespace android;
 
+#define FIND_CLASS(var, className) \
+        var = env->FindClass(className); \
+        LOG_FATAL_IF(! var, "Unable to find class " className); \
+        var = jclass(env->NewGlobalRef(var));
+
+#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
+        var = env->GetMethodID(clazz, methodName, methodDescriptor); \
+        LOG_FATAL_IF(! var, "Unable to find method " methodName);
+
 int register_android_hardware_SensorManager(JNIEnv *env)
 {
-    return jniRegisterNativeMethods(env, "android/hardware/SystemSensorManager",
-            gMethods, NELEM(gMethods));
+    jniRegisterNativeMethods(env, "android/hardware/SystemSensorManager",
+            gSystemSensorManagerMethods, NELEM(gSystemSensorManagerMethods));
+
+    jniRegisterNativeMethods(env, "android/hardware/SystemSensorManager$SensorEventQueue",
+            gSensorEventQueueMethods, NELEM(gSensorEventQueueMethods));
+
+    FIND_CLASS(gSensorEventQueueClassInfo.clazz, "android/hardware/SystemSensorManager$SensorEventQueue");
+
+    GET_METHOD_ID(gSensorEventQueueClassInfo.dispatchSensorEvent,
+            gSensorEventQueueClassInfo.clazz,
+            "dispatchSensorEvent", "(I[FIJ)V");
+
+    return 0;
 }
diff --git a/core/jni/android_net_TrafficStats.cpp b/core/jni/android_net_TrafficStats.cpp
index 325fe26..aa8a58b 100644
--- a/core/jni/android_net_TrafficStats.cpp
+++ b/core/jni/android_net_TrafficStats.cpp
@@ -31,8 +31,7 @@
 
 namespace android {
 
-static const uint64_t VALUE_UNKNOWN = -1;
-static const char* IFACE_STAT_ALL = "/proc/net/xt_qtaguid/iface_stat_all";
+static const char* QTAGUID_IFACE_STATS = "/proc/net/xt_qtaguid/iface_stat_fmt";
 
 enum Tx_Rx {
     TX,
@@ -46,20 +45,45 @@
 };
 
 // NOTE: keep these in sync with TrafficStats.java
-enum IfaceStatType {
+static const uint64_t UNKNOWN = -1;
+
+enum StatsType {
     RX_BYTES = 0,
     RX_PACKETS = 1,
     TX_BYTES = 2,
-    TX_PACKETS = 3
+    TX_PACKETS = 3,
+    TCP_RX_PACKETS = 4,
+    TCP_TX_PACKETS = 5
 };
 
-struct IfaceStat {
+struct Stats {
     uint64_t rxBytes;
     uint64_t rxPackets;
     uint64_t txBytes;
     uint64_t txPackets;
+    uint64_t tcpRxPackets;
+    uint64_t tcpTxPackets;
 };
 
+static uint64_t getStatsType(struct Stats* stats, StatsType type) {
+    switch (type) {
+        case RX_BYTES:
+            return stats->rxBytes;
+        case RX_PACKETS:
+            return stats->rxPackets;
+        case TX_BYTES:
+            return stats->txBytes;
+        case TX_PACKETS:
+            return stats->txPackets;
+        case TCP_RX_PACKETS:
+            return stats->tcpRxPackets;
+        case TCP_TX_PACKETS:
+            return stats->tcpTxPackets;
+        default:
+            return UNKNOWN;
+    }
+}
+
 // Returns an ASCII decimal number read from the specified file, -1 on error.
 static jlong readNumber(char const* filename) {
     char buf[80];
@@ -81,79 +105,72 @@
     return atoll(buf);
 }
 
-static int parseIfaceStat(const char* iface, struct IfaceStat* stat) {
-    FILE *fp = fopen(IFACE_STAT_ALL, "r");
-    if (!fp) {
-        return errno;
+static int parseIfaceStats(const char* iface, struct Stats* stats) {
+    FILE *fp = fopen(QTAGUID_IFACE_STATS, "r");
+    if (fp == NULL) {
+        return -1;
     }
 
     char buffer[256];
     char cur_iface[32];
-    int active;
-    uint64_t rxBytes, rxPackets, txBytes, txPackets, devRxBytes, devRxPackets, devTxBytes,
-            devTxPackets;
+    bool foundTcp = false;
+    uint64_t rxBytes, rxPackets, txBytes, txPackets, tcpRxPackets, tcpTxPackets;
 
-    while (fgets(buffer, 256, fp) != NULL) {
-        if (sscanf(buffer, "%31s %d %llu %llu %llu %llu %llu %llu %llu %llu", cur_iface, &active,
-                   &rxBytes, &rxPackets, &txBytes, &txPackets, &devRxBytes, &devRxPackets,
-                   &devTxBytes, &devTxPackets) != 10) {
-            continue;
-        }
-
-        if (!iface || !strcmp(iface, cur_iface)) {
-            stat->rxBytes += rxBytes;
-            stat->rxPackets += rxPackets;
-            stat->txBytes += txBytes;
-            stat->txPackets += txPackets;
-
-            if (active) {
-                stat->rxBytes += devRxBytes;
-                stat->rxPackets += devRxPackets;
-                stat->txBytes += devTxBytes;
-                stat->txPackets += devTxPackets;
+    while (fgets(buffer, sizeof(buffer), fp) != NULL) {
+        int matched = sscanf(buffer, "%31s %llu %llu %llu %llu "
+                "%*llu %llu %*llu %*llu %*llu %*llu "
+                "%*llu %llu %*llu %*llu %*llu %*llu", cur_iface, &rxBytes,
+                &rxPackets, &txBytes, &txPackets, &tcpRxPackets, &tcpTxPackets);
+        if (matched >= 5) {
+            if (matched == 7) {
+                foundTcp = true;
+            }
+            if (!iface || !strcmp(iface, cur_iface)) {
+                stats->rxBytes += rxBytes;
+                stats->rxPackets += rxPackets;
+                stats->txBytes += txBytes;
+                stats->txPackets += txPackets;
+                if (matched == 7) {
+                    stats->tcpRxPackets += tcpRxPackets;
+                    stats->tcpTxPackets += tcpTxPackets;
+                }
             }
         }
     }
 
-    fclose(fp);
+    if (!foundTcp) {
+        stats->tcpRxPackets = UNKNOWN;
+        stats->tcpTxPackets = UNKNOWN;
+    }
+
+    if (fclose(fp) != 0) {
+        return -1;
+    }
     return 0;
 }
 
-static uint64_t getIfaceStatType(const char* iface, IfaceStatType type) {
-    struct IfaceStat stat;
-    memset(&stat, 0, sizeof(IfaceStat));
-
-    if (parseIfaceStat(iface, &stat)) {
-        return VALUE_UNKNOWN;
-    }
-
-    switch (type) {
-        case RX_BYTES:
-            return stat.rxBytes;
-        case RX_PACKETS:
-            return stat.rxPackets;
-        case TX_BYTES:
-            return stat.txBytes;
-        case TX_PACKETS:
-            return stat.txPackets;
-        default:
-            return VALUE_UNKNOWN;
-    }
-}
-
 static jlong getTotalStat(JNIEnv* env, jclass clazz, jint type) {
-    return getIfaceStatType(NULL, (IfaceStatType) type);
+    struct Stats stats;
+    memset(&stats, 0, sizeof(Stats));
+    if (parseIfaceStats(NULL, &stats) == 0) {
+        return getStatsType(&stats, (StatsType) type);
+    } else {
+        return UNKNOWN;
+    }
 }
 
 static jlong getIfaceStat(JNIEnv* env, jclass clazz, jstring iface, jint type) {
-    struct IfaceStat stat;
-    const char* ifaceChars = env->GetStringUTFChars(iface, NULL);
-    if (ifaceChars) {
-        uint64_t stat = getIfaceStatType(ifaceChars, (IfaceStatType) type);
-        env->ReleaseStringUTFChars(iface, ifaceChars);
-        return stat;
+    ScopedUtfChars iface8(env, iface);
+    if (iface8.c_str() == NULL) {
+        return UNKNOWN;
+    }
+
+    struct Stats stats;
+    memset(&stats, 0, sizeof(Stats));
+    if (parseIfaceStats(NULL, &stats) == 0) {
+        return getStatsType(&stats, (StatsType) type);
     } else {
-        return VALUE_UNKNOWN;
+        return UNKNOWN;
     }
 }
 
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 33ed0b9..895a0dc 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -269,8 +269,7 @@
 // ----------------------------------------------------------------------------
 
 static bool android_view_GLES20Canvas_quickReject(JNIEnv* env, jobject clazz,
-        OpenGLRenderer* renderer, jfloat left, jfloat top, jfloat right, jfloat bottom,
-        SkCanvas::EdgeType edge) {
+        OpenGLRenderer* renderer, jfloat left, jfloat top, jfloat right, jfloat bottom) {
     return renderer->quickReject(left, top, right, bottom);
 }
 
@@ -981,7 +980,7 @@
     { "nSaveLayerAlpha",    "(IFFFFII)I",      (void*) android_view_GLES20Canvas_saveLayerAlpha },
     { "nSaveLayerAlpha",    "(III)I",          (void*) android_view_GLES20Canvas_saveLayerAlphaClip },
 
-    { "nQuickReject",       "(IFFFFI)Z",       (void*) android_view_GLES20Canvas_quickReject },
+    { "nQuickReject",       "(IFFFF)Z",        (void*) android_view_GLES20Canvas_quickReject },
     { "nClipRect",          "(IFFFFI)Z",       (void*) android_view_GLES20Canvas_clipRectF },
     { "nClipRect",          "(IIIIII)Z",       (void*) android_view_GLES20Canvas_clipRect },
     { "nClipPath",          "(III)Z",          (void*) android_view_GLES20Canvas_clipPath },
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 1f15370..ed92e43 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -66,7 +66,6 @@
     jfieldID mNativeSurfaceControl;
     jfieldID mGenerationId;
     jfieldID mCanvas;
-    jfieldID mCanvasSaveCount;
     jmethodID ctor;
 } gSurfaceClassInfo;
 
@@ -78,11 +77,16 @@
 } gRectClassInfo;
 
 static struct {
+    jfieldID mFinalizer;
     jfieldID mNativeCanvas;
     jfieldID mSurfaceFormat;
 } gCanvasClassInfo;
 
 static struct {
+    jfieldID mNativeCanvas;
+} gCanvasFinalizerClassInfo;
+
+static struct {
     jfieldID width;
     jfieldID height;
     jfieldID refreshRate;
@@ -135,6 +139,7 @@
         return mScreenshot.getFormat();
     }
 
+    SK_DECLARE_UNFLATTENABLE_OBJECT()
 protected:
     // overrides from SkPixelRef
     virtual void* onLockPixels(SkColorTable** ct) {
@@ -373,6 +378,15 @@
     }
 }
 
+static inline void swapCanvasPtr(JNIEnv* env, jobject canvasObj, SkCanvas* newCanvas) {
+  jobject canvasFinalizerObj = env->GetObjectField(canvasObj, gCanvasClassInfo.mFinalizer);
+  SkCanvas* previousCanvas = reinterpret_cast<SkCanvas*>(
+          env->GetIntField(canvasObj, gCanvasClassInfo.mNativeCanvas));
+  env->SetIntField(canvasObj, gCanvasClassInfo.mNativeCanvas, (int)newCanvas);
+  env->SetIntField(canvasFinalizerObj, gCanvasFinalizerClassInfo.mNativeCanvas, (int)newCanvas);
+  SkSafeUnref(previousCanvas);
+}
+
 static jobject nativeLockCanvas(JNIEnv* env, jobject surfaceObj, jobject dirtyRectObj) {
     sp<Surface> surface(getSurface(env, surfaceObj));
     if (!Surface::isValid(surface)) {
@@ -409,8 +423,6 @@
     jobject canvasObj = env->GetObjectField(surfaceObj, gSurfaceClassInfo.mCanvas);
     env->SetIntField(canvasObj, gCanvasClassInfo.mSurfaceFormat, info.format);
 
-    SkCanvas* nativeCanvas = reinterpret_cast<SkCanvas*>(
-            env->GetIntField(canvasObj, gCanvasClassInfo.mNativeCanvas));
     SkBitmap bitmap;
     ssize_t bpr = info.s * bytesPerPixel(info.format);
     bitmap.setConfig(convertPixelFormat(info.format), info.w, info.h, bpr);
@@ -423,7 +435,9 @@
         // be safe with an empty bitmap.
         bitmap.setPixels(NULL);
     }
-    nativeCanvas->setBitmapDevice(bitmap);
+
+    SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (bitmap));
+    swapCanvasPtr(env, canvasObj, nativeCanvas);
 
     SkRegion clipReg;
     if (dirtyRegion.isRect()) { // very common case
@@ -440,9 +454,6 @@
 
     nativeCanvas->clipRegion(clipReg);
 
-    int saveCount = nativeCanvas->save();
-    env->SetIntField(surfaceObj, gSurfaceClassInfo.mCanvasSaveCount, saveCount);
-
     if (dirtyRectObj) {
         const Rect& bounds(dirtyRegion.getBounds());
         env->SetIntField(dirtyRectObj, gRectClassInfo.left, bounds.left);
@@ -467,12 +478,8 @@
     }
 
     // detach the canvas from the surface
-    SkCanvas* nativeCanvas = reinterpret_cast<SkCanvas*>(
-            env->GetIntField(canvasObj, gCanvasClassInfo.mNativeCanvas));
-    int saveCount = env->GetIntField(surfaceObj, gSurfaceClassInfo.mCanvasSaveCount);
-    nativeCanvas->restoreToCount(saveCount);
-    nativeCanvas->setBitmapDevice(SkBitmap());
-    env->SetIntField(surfaceObj, gSurfaceClassInfo.mCanvasSaveCount, 0);
+    SkCanvas* nativeCanvas = SkNEW(SkCanvas);
+    swapCanvasPtr(env, canvasObj, nativeCanvas);
 
     // unlock surface
     status_t err = surface->unlockAndPost();
@@ -888,14 +895,16 @@
             env->GetFieldID(gSurfaceClassInfo.clazz, "mGenerationId", "I");
     gSurfaceClassInfo.mCanvas =
             env->GetFieldID(gSurfaceClassInfo.clazz, "mCanvas", "Landroid/graphics/Canvas;");
-    gSurfaceClassInfo.mCanvasSaveCount =
-            env->GetFieldID(gSurfaceClassInfo.clazz, "mCanvasSaveCount", "I");
     gSurfaceClassInfo.ctor = env->GetMethodID(gSurfaceClassInfo.clazz, "<init>", "()V");
 
     clazz = env->FindClass("android/graphics/Canvas");
+    gCanvasClassInfo.mFinalizer = env->GetFieldID(clazz, "mFinalizer", "Landroid/graphics/Canvas$CanvasFinalizer;");
     gCanvasClassInfo.mNativeCanvas = env->GetFieldID(clazz, "mNativeCanvas", "I");
     gCanvasClassInfo.mSurfaceFormat = env->GetFieldID(clazz, "mSurfaceFormat", "I");
 
+    clazz = env->FindClass("android/graphics/Canvas$CanvasFinalizer");
+    gCanvasFinalizerClassInfo.mNativeCanvas = env->GetFieldID(clazz, "mNativeCanvas", "I");
+
     clazz = env->FindClass("android/graphics/Rect");
     gRectClassInfo.left = env->GetFieldID(clazz, "left", "I");
     gRectClassInfo.top = env->GetFieldID(clazz, "top", "I");
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index 87b312f..64cbda3 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -43,11 +43,16 @@
 } gRectClassInfo;
 
 static struct {
-    jfieldID nativeCanvas;
-    jfieldID surfaceFormat;
+    jfieldID mFinalizer;
+    jfieldID mNativeCanvas;
+    jfieldID mSurfaceFormat;
 } gCanvasClassInfo;
 
 static struct {
+    jfieldID mNativeCanvas;
+} gCanvasFinalizerClassInfo;
+
+static struct {
     jfieldID nativeWindow;
 } gTextureViewClassInfo;
 
@@ -120,6 +125,15 @@
     }
 }
 
+static inline void swapCanvasPtr(JNIEnv* env, jobject canvasObj, SkCanvas* newCanvas) {
+  jobject canvasFinalizerObj = env->GetObjectField(canvasObj, gCanvasClassInfo.mFinalizer);
+  SkCanvas* previousCanvas = reinterpret_cast<SkCanvas*>(
+          env->GetIntField(canvasObj, gCanvasClassInfo.mNativeCanvas));
+  env->SetIntField(canvasObj, gCanvasClassInfo.mNativeCanvas, (int)newCanvas);
+  env->SetIntField(canvasFinalizerObj, gCanvasFinalizerClassInfo.mNativeCanvas, (int)newCanvas);
+  SkSafeUnref(previousCanvas);
+}
+
 static void android_view_TextureView_lockCanvas(JNIEnv* env, jobject,
         jint nativeWindow, jobject canvas, jobject dirtyRect) {
 
@@ -157,9 +171,10 @@
         bitmap.setPixels(NULL);
     }
 
-    SET_INT(canvas, gCanvasClassInfo.surfaceFormat, buffer.format);
-    SkCanvas* nativeCanvas = (SkCanvas*) GET_INT(canvas, gCanvasClassInfo.nativeCanvas);
-    nativeCanvas->setBitmapDevice(bitmap);
+    SET_INT(canvas, gCanvasClassInfo.mSurfaceFormat, buffer.format);
+
+    SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (bitmap));
+    swapCanvasPtr(env, canvas, nativeCanvas);
 
     SkRect clipRect;
     clipRect.set(rect.left, rect.top, rect.right, rect.bottom);
@@ -174,8 +189,8 @@
 static void android_view_TextureView_unlockCanvasAndPost(JNIEnv* env, jobject,
         jint nativeWindow, jobject canvas) {
 
-    SkCanvas* nativeCanvas = (SkCanvas*) GET_INT(canvas, gCanvasClassInfo.nativeCanvas);
-    nativeCanvas->setBitmapDevice(SkBitmap());
+    SkCanvas* nativeCanvas = SkNEW(SkCanvas);
+    swapCanvasPtr(env, canvas, nativeCanvas);
 
     if (nativeWindow) {
         sp<ANativeWindow> window((ANativeWindow*) nativeWindow);
@@ -226,8 +241,12 @@
     GET_FIELD_ID(gRectClassInfo.bottom, clazz, "bottom", "I");
 
     FIND_CLASS(clazz, "android/graphics/Canvas");
-    GET_FIELD_ID(gCanvasClassInfo.nativeCanvas, clazz, "mNativeCanvas", "I");
-    GET_FIELD_ID(gCanvasClassInfo.surfaceFormat, clazz, "mSurfaceFormat", "I");
+    GET_FIELD_ID(gCanvasClassInfo.mFinalizer, clazz, "mFinalizer", "Landroid/graphics/Canvas$CanvasFinalizer;");
+    GET_FIELD_ID(gCanvasClassInfo.mNativeCanvas, clazz, "mNativeCanvas", "I");
+    GET_FIELD_ID(gCanvasClassInfo.mSurfaceFormat, clazz, "mSurfaceFormat", "I");
+
+    FIND_CLASS(clazz, "android/graphics/Canvas$CanvasFinalizer");
+    GET_FIELD_ID(gCanvasFinalizerClassInfo.mNativeCanvas, clazz, "mNativeCanvas", "I");
 
     FIND_CLASS(clazz, "android/view/TextureView");
     GET_FIELD_ID(gTextureViewClassInfo.nativeWindow, clazz, "mNativeWindow", "I");
diff --git a/core/tests/coretests/src/android/provider/SettingsProviderTest.java b/core/tests/coretests/src/android/provider/SettingsProviderTest.java
index 6edd2dc..131651a 100644
--- a/core/tests/coretests/src/android/provider/SettingsProviderTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsProviderTest.java
@@ -132,6 +132,42 @@
     }
 
     @MediumTest
+    public void testSettingsChangeForOtherUser() {
+        UserManager um = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
+        ContentResolver r = getContext().getContentResolver();
+
+        // Make sure there's an owner
+        assertTrue(findUser(um, UserHandle.USER_OWNER));
+
+        // create a new user to use for testing
+        UserInfo otherUser = um.createUser("TestUser1", UserInfo.FLAG_GUEST);
+        assertTrue(otherUser != null);
+        try {
+            assertNotSame("Current calling user id should not be the new guest user",
+                    otherUser.id, UserHandle.getCallingUserId());
+
+            Settings.Secure.putString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "gps");
+            Settings.Secure.putStringForUser(r,
+                    Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "network", otherUser.id);
+
+            assertEquals("gps",
+                    Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED));
+            assertEquals("network", Settings.Secure.getStringForUser(
+                    r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, otherUser.id));
+
+            assertNotSame("Current calling user id should not be the new guest user",
+                    otherUser.id, UserHandle.getCallingUserId());
+            Settings.Secure.setLocationProviderEnabledForUser(r, "network", false, otherUser.id);
+            assertEquals("", Settings.Secure.getStringForUser(
+                    r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, otherUser.id));
+
+        } finally {
+            // Tidy up
+            um.removeUser(otherUser.id);
+        }
+    }
+
+    @MediumTest
     public void testRowNumberContentUri() {
         ContentResolver r = getContext().getContentResolver();
 
diff --git a/docs/html/about/versions/android-3.0.jd b/docs/html/about/versions/android-3.0.jd
index 68ac03a..d0b41d3 100644
--- a/docs/html/about/versions/android-3.0.jd
+++ b/docs/html/about/versions/android-3.0.jd
@@ -571,7 +571,7 @@
 can accelerate graphics operations and data processing. Renderscript is an ideal way to create
 high-performance 3D effects for applications, wallpapers, carousels, and more.</p>
 <p>For more information, see the <a
-href="{@docRoot}guide/topics/graphics/renderscript.html">3D Rendering and Computation with
+href="{@docRoot}guide/topics/renderscript/index.html">3D Rendering and Computation with
 Renderscript</a> documentation.</p></li>
 </ul>
 
diff --git a/docs/html/sitemap.txt b/docs/html/sitemap.txt
index 3248b7d..105f60d 100644
--- a/docs/html/sitemap.txt
+++ b/docs/html/sitemap.txt
@@ -2611,7 +2611,7 @@
 http://developer.android.com/reference/android/support/v4/view/PagerTabStrip.html
 http://developer.android.com/reference/android/support/v4/view/PagerTitleStrip.html
 http://developer.android.com/reference/android/renderscript/RSSurfaceView.html
-http://developer.android.com/guide/topics/graphics/renderscript.html
+http://developer.android.com/guide/topics/renderscript/index.html
 http://developer.android.com/reference/android/renderscript/RSTextureView.html
 http://developer.android.com/reference/android/appwidget/AppWidgetHost.html
 http://developer.android.com/reference/javax/xml/XMLConstants.html
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 3949afd..483d11a 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -37,8 +37,8 @@
  * Canvas and Drawables</a> developer guide.</p></div>
  */
 public class Canvas {
-    // assigned in constructors, freed in finalizer
-    final int mNativeCanvas;
+    // assigned in constructors or setBitmap, freed in finalizer
+    int mNativeCanvas;
     
     // may be null
     private Bitmap mBitmap;
@@ -83,7 +83,7 @@
     private final CanvasFinalizer mFinalizer;
 
     private static class CanvasFinalizer {
-        private final int mNativeCanvas;
+        private int mNativeCanvas;
 
         public CanvasFinalizer(int nativeCanvas) {
             mNativeCanvas = nativeCanvas;
@@ -143,6 +143,17 @@
     }
 
     /**
+     * Replace existing canvas while ensuring that the swap has occurred before
+     * the previous native canvas is unreferenced.
+     */
+    private void safeCanvasSwap(int nativeCanvas) {
+        final int oldCanvas = mNativeCanvas;
+        mNativeCanvas = nativeCanvas;
+        mFinalizer.mNativeCanvas = nativeCanvas;
+        finalizer(oldCanvas);
+    }
+    
+    /**
      * Returns null.
      * 
      * @deprecated This method is not supported and should not be invoked.
@@ -168,11 +179,11 @@
     }
 
     /**
-     * Specify a bitmap for the canvas to draw into.  As a side-effect, also
-     * updates the canvas's target density to match that of the bitmap.
+     * Specify a bitmap for the canvas to draw into. As a side-effect, the
+     * canvas' target density is updated to match that of the bitmap while all
+     * other state such as the layers, filters, matrix, and clip are reset.
      *
      * @param bitmap Specifies a mutable bitmap for the canvas to draw into.
-     * 
      * @see #setDensity(int)
      * @see #getDensity()
      */
@@ -181,17 +192,19 @@
             throw new RuntimeException("Can't set a bitmap device on a GL canvas");
         }
 
-        int pointer = 0;
-        if (bitmap != null) {
+        if (bitmap == null) {
+            safeCanvasSwap(initRaster(0));
+            mDensity = Bitmap.DENSITY_NONE;
+        } else {
             if (!bitmap.isMutable()) {
                 throw new IllegalStateException();
             }
             throwIfRecycled(bitmap);
+
+            safeCanvasSwap(initRaster(bitmap.ni()));
             mDensity = bitmap.mDensity;
-            pointer = bitmap.ni();
         }
 
-        native_setBitmap(mNativeCanvas, pointer);
         mBitmap = bitmap;
     }
     
@@ -706,7 +719,7 @@
      *              does not intersect with the canvas' clip
      */
     public boolean quickReject(RectF rect, EdgeType type) {
-        return native_quickReject(mNativeCanvas, rect, type.nativeInt);
+        return native_quickReject(mNativeCanvas, rect);
     }
 
     /**
@@ -726,7 +739,7 @@
      *                    does not intersect with the canvas' clip
      */
     public boolean quickReject(Path path, EdgeType type) {
-        return native_quickReject(mNativeCanvas, path.ni(), type.nativeInt);
+        return native_quickReject(mNativeCanvas, path.ni());
     }
 
     /**
@@ -749,9 +762,9 @@
      * @return            true if the rect (transformed by the canvas' matrix)
      *                    does not intersect with the canvas' clip
      */
-    public boolean quickReject(float left, float top, float right, float bottom, EdgeType type) {
-        return native_quickReject(mNativeCanvas, left, top, right, bottom,
-                                  type.nativeInt);
+    public boolean quickReject(float left, float top, float right, float bottom,
+                               EdgeType type) {
+        return native_quickReject(mNativeCanvas, left, top, right, bottom);
     }
 
     /**
@@ -1625,7 +1638,6 @@
     public static native void freeTextLayoutCaches();
 
     private static native int initRaster(int nativeBitmapOrZero);
-    private static native void native_setBitmap(int nativeCanvas, int bitmap);
     private static native int native_saveLayer(int nativeCanvas, RectF bounds,
                                                int paint, int layerFlags);
     private static native int native_saveLayer(int nativeCanvas, float l,
@@ -1656,15 +1668,12 @@
                                                        Rect bounds);
     private static native void native_getCTM(int canvas, int matrix);
     private static native boolean native_quickReject(int nativeCanvas,
-                                                     RectF rect,
-                                                     int native_edgeType);
+                                                     RectF rect);
     private static native boolean native_quickReject(int nativeCanvas,
-                                                     int path,
-                                                     int native_edgeType);
+                                                     int path);
     private static native boolean native_quickReject(int nativeCanvas,
                                                      float left, float top,
-                                                     float right, float bottom,
-                                                     int native_edgeType);
+                                                     float right, float bottom);
     private static native void native_drawRGB(int nativeCanvas, int r, int g,
                                               int b);
     private static native void native_drawARGB(int nativeCanvas, int a, int r,
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index f6b5ffc..157c7d1 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -375,9 +375,9 @@
      */
     public enum Direction {
         /** clockwise */
-        CW  (0),    // must match enum in SkPath.h
+        CW  (1),    // must match enum in SkPath.h
         /** counter-clockwise */
-        CCW (1);    // must match enum in SkPath.h
+        CCW (2);    // must match enum in SkPath.h
         
         Direction(int ni) {
             nativeInt = ni;
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 4487a3c..c68c9f7 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -225,16 +225,4 @@
     private static native int  nativeGetStyle(int native_instance);
     private static native int  nativeCreateFromAsset(AssetManager mgr, String path);
     private static native int nativeCreateFromFile(String path);
-
-    /**
-     * Set the global gamma coefficients for black and white text. This call is
-     * usually a no-op in shipping products, and only exists for testing during
-     * development.
-     *
-     * @param blackGamma gamma coefficient for black text
-     * @param whiteGamma gamma coefficient for white text
-     *
-     * @hide - this is just for calibrating devices, not for normal apps
-     */
-    public static native void setGammaForText(float blackGamma, float whiteGamma);
 }
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index 5e9872f..5f0844a 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -67,7 +67,7 @@
  * <div class="special reference">
  * <h3>Developer Guides</h3>
  * <p>For more information about creating an application that uses Renderscript, read the
- * <a href="{@docRoot}guide/topics/graphics/renderscript.html">Renderscript</a> developer guide.</p>
+ * <a href="{@docRoot}guide/topics/renderscript/index.html">Renderscript</a> developer guide.</p>
  * </div>
  **/
 public class Allocation extends BaseObj {
@@ -1466,6 +1466,10 @@
      * Creates a non-mipmapped renderscript allocation to use as a
      * graphics texture from the bitmap referenced by resource id
      *
+     * With target API version 18 or greater, this allocation will be
+     * created with USAGE_SHARED. With target API version 17 or lower,
+     * this allocation will be created with USAGE_GRAPHICS_TEXTURE.
+     *
      * @param rs Context to which the allocation will belong.
      * @param res application resources
      * @param id resource id to load the data from
@@ -1476,6 +1480,11 @@
     static public Allocation createFromBitmapResource(RenderScript rs,
                                                       Resources res,
                                                       int id) {
+        if (rs.getApplicationContext().getApplicationInfo().targetSdkVersion >= 18) {
+            return createFromBitmapResource(rs, res, id,
+                                            MipmapControl.MIPMAP_NONE,
+                                            USAGE_SHARED | USAGE_SCRIPT);
+        }
         return createFromBitmapResource(rs, res, id,
                                         MipmapControl.MIPMAP_NONE,
                                         USAGE_GRAPHICS_TEXTURE);
diff --git a/graphics/java/android/renderscript/Element.java b/graphics/java/android/renderscript/Element.java
index 20e716c..a79ce17 100644
--- a/graphics/java/android/renderscript/Element.java
+++ b/graphics/java/android/renderscript/Element.java
@@ -44,7 +44,7 @@
  * <div class="special reference">
  * <h3>Developer Guides</h3>
  * <p>For more information about creating an application that uses Renderscript, read the
- * <a href="{@docRoot}guide/topics/graphics/renderscript.html">Renderscript</a> developer guide.</p>
+ * <a href="{@docRoot}guide/topics/renderscript/index.html">Renderscript</a> developer guide.</p>
  * </div>
  **/
 public class Element extends BaseObj {
diff --git a/graphics/java/android/renderscript/RSSurfaceView.java b/graphics/java/android/renderscript/RSSurfaceView.java
index 506f1af..82ed95c 100644
--- a/graphics/java/android/renderscript/RSSurfaceView.java
+++ b/graphics/java/android/renderscript/RSSurfaceView.java
@@ -36,7 +36,7 @@
  * <div class="special reference">
  * <h3>Developer Guides</h3>
  * <p>For more information about creating an application that uses Renderscript, read the
- * <a href="{@docRoot}guide/topics/graphics/renderscript.html">Renderscript</a> developer guide.</p>
+ * <a href="{@docRoot}guide/topics/renderscript/index.html">Renderscript</a> developer guide.</p>
  * </div>
  */
 public class RSSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index bfa09c2..00e8769 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -41,7 +41,7 @@
  * <div class="special reference">
  * <h3>Developer Guides</h3>
  * <p>For more information about creating an application that uses Renderscript, read the
- * <a href="{@docRoot}guide/topics/graphics/renderscript.html">Renderscript</a> developer guide.</p>
+ * <a href="{@docRoot}guide/topics/renderscript/index.html">Renderscript</a> developer guide.</p>
  * </div>
  **/
 public class RenderScript {
@@ -157,6 +157,12 @@
         rsnContextFinish(mContext);
     }
 
+    native void rsnContextSendMessage(int con, int id, int[] data);
+    synchronized void nContextSendMessage(int id, int[] data) {
+        validate();
+        rsnContextSendMessage(mContext, id, data);
+    }
+
     native void rsnContextBindRootScript(int con, int script);
     synchronized void nContextBindRootScript(int script) {
         validate();
@@ -824,6 +830,16 @@
     }
 
     /**
+     * @hide
+     *
+     * @param id
+     * @param data
+     */
+    public void sendMessage(int id, int[] data) {
+        nContextSendMessage(id, data);
+    }
+
+    /**
      * Runtime error base class.  An application should derive from this class
      * if it wishes to install an error handler.  When errors occur at runtime
      * the fields in this class will be filled and the run method called.
diff --git a/graphics/java/android/renderscript/RenderScriptGL.java b/graphics/java/android/renderscript/RenderScriptGL.java
index 12c8102..5269405 100644
--- a/graphics/java/android/renderscript/RenderScriptGL.java
+++ b/graphics/java/android/renderscript/RenderScriptGL.java
@@ -39,7 +39,7 @@
  * <div class="special reference">
  * <h3>Developer Guides</h3>
  * <p>For more information about creating an application that uses Renderscript, read the
- * <a href="{@docRoot}guide/topics/graphics/renderscript.html">Renderscript</a> developer guide.</p>
+ * <a href="{@docRoot}guide/topics/renderscript/index.html">Renderscript</a> developer guide.</p>
  * </div>
  **/
 public class RenderScriptGL extends RenderScript {
diff --git a/graphics/java/android/renderscript/Type.java b/graphics/java/android/renderscript/Type.java
index cb12594..d1446f6 100644
--- a/graphics/java/android/renderscript/Type.java
+++ b/graphics/java/android/renderscript/Type.java
@@ -40,7 +40,7 @@
  * <div class="special reference">
  * <h3>Developer Guides</h3>
  * <p>For more information about creating an application that uses Renderscript, read the
- * <a href="{@docRoot}guide/topics/graphics/renderscript.html">Renderscript</a> developer guide.</p>
+ * <a href="{@docRoot}guide/topics/renderscript/index.html">Renderscript</a> developer guide.</p>
  * </div>
  **/
 public class Type extends BaseObj {
diff --git a/graphics/java/android/renderscript/package.html b/graphics/java/android/renderscript/package.html
index 775645f..5eab23c 100644
--- a/graphics/java/android/renderscript/package.html
+++ b/graphics/java/android/renderscript/package.html
@@ -4,7 +4,7 @@
 carrying out mathematical calculations and 3D graphics rendering.</p>
 
 <p>For more information, see the
-<a href="{@docRoot}guide/topics/graphics/renderscript.html">Renderscript</a> developer guide.</p>
+<a href="{@docRoot}guide/topics/renderscript/index.html">Renderscript</a> developer guide.</p>
 {@more}
 
 <p>An example of Renderscript in applications include the 3D carousel view that is present in
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index 54413b4..80001a6 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -346,6 +346,23 @@
     rsContextDeinitToClient(con);
 }
 
+static void
+nContextSendMessage(JNIEnv *_env, jobject _this, RsContext con, jint id, jintArray data)
+{
+    jint *ptr = NULL;
+    jint len = 0;
+    if (data) {
+        len = _env->GetArrayLength(data);
+        jint *ptr = _env->GetIntArrayElements(data, NULL);
+    }
+    LOG_API("nContextSendMessage, con(%p), id(%i), len(%i)", con, id, len);
+    rsContextSendMessage(con, id, (const uint8_t *)ptr, len * sizeof(int));
+    if (data) {
+        _env->ReleaseIntArrayElements(data, ptr, JNI_ABORT);
+    }
+}
+
+
 
 static jint
 nElementCreate(JNIEnv *_env, jobject _this, RsContext con, jint type, jint kind, jboolean norm, jint size)
@@ -1434,6 +1451,7 @@
 {"rsnContextDump",                   "(II)V",                                 (void*)nContextDump },
 {"rsnContextPause",                  "(I)V",                                  (void*)nContextPause },
 {"rsnContextResume",                 "(I)V",                                  (void*)nContextResume },
+{"rsnContextSendMessage",            "(II[I)V",                               (void*)nContextSendMessage },
 {"rsnAssignName",                    "(II[B)V",                               (void*)nAssignName },
 {"rsnGetName",                       "(II)Ljava/lang/String;",                (void*)nGetName },
 {"rsnObjDestroy",                    "(II)V",                                 (void*)nObjDestroy },
diff --git a/libs/androidfw/Input.cpp b/libs/androidfw/Input.cpp
index 8996f6f..eca692a 100644
--- a/libs/androidfw/Input.cpp
+++ b/libs/androidfw/Input.cpp
@@ -225,7 +225,7 @@
     }
 
     for (uint32_t i = 0; i < count; i++) {
-        values[i] = parcel->readInt32();
+        values[i] = parcel->readFloat();
     }
     return OK;
 }
@@ -235,7 +235,7 @@
 
     uint32_t count = __builtin_popcountll(bits);
     for (uint32_t i = 0; i < count; i++) {
-        parcel->writeInt32(values[i]);
+        parcel->writeFloat(values[i]);
     }
     return OK;
 }
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 549edd2..881d160 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -21,10 +21,10 @@
 		LayerRenderer.cpp \
 		Matrix.cpp \
 		OpenGLRenderer.cpp \
-		PathRenderer.cpp \
 		Patch.cpp \
 		PatchCache.cpp \
 		PathCache.cpp \
+		PathTessellator.cpp \
 		Program.cpp \
 		ProgramCache.cpp \
 		ResourceCache.cpp \
@@ -42,6 +42,7 @@
 		external/skia/include/core \
 		external/skia/include/effects \
 		external/skia/include/images \
+		external/skia/src/core \
 		external/skia/src/ports \
 		external/skia/include/utils
 
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 7e645d2..1a86b3a 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -89,6 +89,9 @@
 
     mFunctorsCount = 0;
 
+    debugLayersUpdates = false;
+    debugOverdraw = false;
+
     mInitialized = true;
 }
 
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 1c4d05f..d73ac1e 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -66,7 +66,6 @@
 static const GLsizei gMeshStride = sizeof(TextureVertex);
 static const GLsizei gVertexStride = sizeof(Vertex);
 static const GLsizei gAlphaVertexStride = sizeof(AlphaVertex);
-static const GLsizei gAAVertexStride = sizeof(AAVertex);
 static const GLsizei gMeshTextureOffset = 2 * sizeof(float);
 static const GLsizei gVertexAlphaOffset = 2 * sizeof(float);
 static const GLsizei gVertexAAWidthOffset = 2 * sizeof(float);
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index b25288b..f3bd188 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -18,7 +18,8 @@
 #define ANDROID_HWUI_DISPLAY_LIST_RENDERER_H
 
 #include <SkChunkAlloc.h>
-#include <SkFlattenable.h>
+#include <SkReader32.h>
+#include <SkWriter32.h>
 #include <SkMatrix.h>
 #include <SkCamera.h>
 #include <SkPaint.h>
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 97988f7..d8297da 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -16,6 +16,7 @@
 
 #define LOG_TAG "OpenGLRenderer"
 
+#include <SkGlyph.h>
 #include <SkUtils.h>
 
 #include <cutils/properties.h>
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index e8a85fd..a580d89 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -23,6 +23,7 @@
 
 #include <ui/Region.h>
 
+#include <SkPaint.h>
 #include <SkXfermode.h>
 
 #include "Rect.h"
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 45569ac..1a760a8 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -33,7 +33,7 @@
 
 #include "OpenGLRenderer.h"
 #include "DisplayListRenderer.h"
-#include "PathRenderer.h"
+#include "PathTessellator.h"
 #include "Properties.h"
 #include "Vector.h"
 
@@ -80,7 +80,7 @@
     { SkXfermode::kDstATop_Mode,  GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
     { SkXfermode::kXor_Mode,      GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
     { SkXfermode::kPlus_Mode,     GL_ONE,                 GL_ONE },
-    { SkXfermode::kMultiply_Mode, GL_ZERO,                GL_SRC_COLOR },
+    { SkXfermode::kModulate_Mode, GL_ZERO,                GL_SRC_COLOR },
     { SkXfermode::kScreen_Mode,   GL_ONE,                 GL_ONE_MINUS_SRC_COLOR }
 };
 
@@ -101,7 +101,7 @@
     { SkXfermode::kDstATop_Mode,  GL_DST_ALPHA,           GL_ONE_MINUS_SRC_ALPHA },
     { SkXfermode::kXor_Mode,      GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
     { SkXfermode::kPlus_Mode,     GL_ONE,                 GL_ONE },
-    { SkXfermode::kMultiply_Mode, GL_DST_COLOR,           GL_ZERO },
+    { SkXfermode::kModulate_Mode, GL_DST_COLOR,           GL_ZERO },
     { SkXfermode::kScreen_Mode,   GL_ONE_MINUS_DST_COLOR, GL_ONE }
 };
 
@@ -1483,10 +1483,6 @@
     mDescription.isAA = true;
 }
 
-void OpenGLRenderer::setupDrawVertexShape() {
-    mDescription.isVertexShape = true;
-}
-
 void OpenGLRenderer::setupDrawPoint(float pointSize) {
     mDescription.isPoint = true;
     mDescription.pointSize = pointSize;
@@ -1702,41 +1698,6 @@
     mCaches.unbindIndicesBuffer();
 }
 
-/**
- * Sets up the shader to draw an AA line. We draw AA lines with quads, where there is an
- * outer boundary that fades out to 0. The variables set in the shader define the proportion of
- * the width and length of the primitive occupied by the AA region. The vtxWidth and vtxLength
- * attributes (one per vertex) are values from zero to one that tells the fragment
- * shader where the fragment is in relation to the line width/length overall; these values are
- * then used to compute the proper color, based on whether the fragment lies in the fading AA
- * region of the line.
- * Note that we only pass down the width values in this setup function. The length coordinates
- * are set up for each individual segment.
- */
-void OpenGLRenderer::setupDrawAALine(GLvoid* vertices, GLvoid* widthCoords,
-        GLvoid* lengthCoords, float boundaryWidthProportion, int& widthSlot, int& lengthSlot) {
-    bool force = mCaches.unbindMeshBuffer();
-    mCaches.bindPositionVertexPointer(force, vertices, gAAVertexStride);
-    mCaches.resetTexCoordsVertexPointer();
-    mCaches.unbindIndicesBuffer();
-
-    widthSlot = mCaches.currentProgram->getAttrib("vtxWidth");
-    glEnableVertexAttribArray(widthSlot);
-    glVertexAttribPointer(widthSlot, 1, GL_FLOAT, GL_FALSE, gAAVertexStride, widthCoords);
-
-    lengthSlot = mCaches.currentProgram->getAttrib("vtxLength");
-    glEnableVertexAttribArray(lengthSlot);
-    glVertexAttribPointer(lengthSlot, 1, GL_FLOAT, GL_FALSE, gAAVertexStride, lengthCoords);
-
-    int boundaryWidthSlot = mCaches.currentProgram->getUniform("boundaryWidth");
-    glUniform1f(boundaryWidthSlot, boundaryWidthProportion);
-}
-
-void OpenGLRenderer::finishDrawAALine(const int widthSlot, const int lengthSlot) {
-    glDisableVertexAttribArray(widthSlot);
-    glDisableVertexAttribArray(lengthSlot);
-}
-
 void OpenGLRenderer::finishDrawTexture() {
 }
 
@@ -2097,39 +2058,26 @@
     return DrawGlInfo::kStatusDrew;
 }
 
-/**
- * Renders a convex path via tessellation. For AA paths, this function uses a similar approach to
- * that of AA lines in the drawLines() function.  We expand the convex path by a half pixel in
- * screen space in all directions. However, instead of using a fragment shader to compute the
- * translucency of the color from its position, we simply use a varying parameter to define how far
- * a given pixel is from the edge. For non-AA paths, the expansion and alpha varying are not used.
- *
- * Doesn't yet support joins, caps, or path effects.
- */
-void OpenGLRenderer::drawConvexPath(const SkPath& path, SkPaint* paint) {
+status_t OpenGLRenderer::drawVertexBuffer(const VertexBuffer& vertexBuffer, SkPaint* paint,
+        bool useOffset) {
+    if (!vertexBuffer.getSize()) {
+        // no vertices to draw
+        return DrawGlInfo::kStatusDone;
+    }
+
     int color = paint->getColor();
     SkXfermode::Mode mode = getXfermode(paint->getXfermode());
     bool isAA = paint->isAntiAlias();
 
-    VertexBuffer vertexBuffer;
-    // TODO: try clipping large paths to viewport
-    PathRenderer::convexPathVertices(path, paint, mSnapshot->transform, vertexBuffer);
-
-    if (!vertexBuffer.getSize()) {
-        // no vertices to draw
-        return;
-    }
-
     setupDraw();
     setupDrawNoTexture();
     if (isAA) setupDrawAA();
-    setupDrawVertexShape();
     setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha);
     setupDrawColorFilter();
     setupDrawShader();
     setupDrawBlending(isAA, mode);
     setupDrawProgram();
-    setupDrawModelViewIdentity();
+    setupDrawModelViewIdentity(useOffset);
     setupDrawColorUniforms();
     setupDrawColorFilterUniforms();
     setupDrawShaderIdentityUniforms();
@@ -2150,286 +2098,59 @@
         glVertexAttribPointer(alphaSlot, 1, GL_FLOAT, GL_FALSE, gAlphaVertexStride, alphaCoords);
     }
 
-    SkRect bounds = PathRenderer::computePathBounds(path, paint);
-    dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, *mSnapshot->transform);
-
     glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexBuffer.getSize());
 
     if (isAA) {
         glDisableVertexAttribArray(alphaSlot);
     }
+
+    return DrawGlInfo::kStatusDrew;
 }
 
 /**
- * We draw lines as quads (tristrips). Using GL_LINES can be difficult because the rasterization
- * rules for those lines produces some unexpected results, and may vary between hardware devices.
- * The basics of lines-as-quads is easy; we simply find the normal to the line and position the
- * corners of the quads on either side of each line endpoint, separated by the strokeWidth
- * of the line. Hairlines are more involved because we need to account for transform scaling
- * to end up with a one-pixel-wide line in screen space..
- * Anti-aliased lines add another factor to the approach. We use a specialized fragment shader
- * in combination with values that we calculate and pass down in this method. The basic approach
- * is that the quad we create contains both the core line area plus a bounding area in which
- * the translucent/AA pixels are drawn. The values we calculate tell the shader what
- * proportion of the width and the length of a given segment is represented by the boundary
- * region. The quad ends up being exactly .5 pixel larger in all directions than the non-AA quad.
- * The bounding region is actually 1 pixel wide on all sides (half pixel on the outside, half pixel
- * on the inside). This ends up giving the result we want, with pixels that are completely
- * 'inside' the line area being filled opaquely and the other pixels being filled according to
- * how far into the boundary region they are, which is determined by shader interpolation.
+ * Renders a convex path via tessellation. For AA paths, this function uses a similar approach to
+ * that of AA lines in the drawLines() function.  We expand the convex path by a half pixel in
+ * screen space in all directions. However, instead of using a fragment shader to compute the
+ * translucency of the color from its position, we simply use a varying parameter to define how far
+ * a given pixel is from the edge. For non-AA paths, the expansion and alpha varying are not used.
+ *
+ * Doesn't yet support joins, caps, or path effects.
+ */
+status_t OpenGLRenderer::drawConvexPath(const SkPath& path, SkPaint* paint) {
+    VertexBuffer vertexBuffer;
+    // 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, *mSnapshot->transform);
+
+    return drawVertexBuffer(vertexBuffer, paint);
+}
+
+/**
+ * We create tristrips for the lines much like shape stroke tessellation, using a per-vertex alpha
+ * and additional geometry for defining an alpha slope perimeter.
+ *
+ * Using GL_LINES can be difficult because the rasterization rules for those lines produces some
+ * unexpected results, and may vary between hardware devices. Previously we used a varying-base
+ * in-shader alpha region, but found it to be taxing on some GPUs.
+ *
+ * TODO: try using a fixed input buffer for non-capped lines as in text rendering. this may reduce
+ * memory transfer by removing need for degenerate vertices.
  */
 status_t OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) {
-    if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
+    if (mSnapshot->isIgnored() || count < 4) return DrawGlInfo::kStatusDone;
 
-    const bool isAA = paint->isAntiAlias();
-    // We use half the stroke width here because we're going to position the quad
-    // corner vertices half of the width away from the line endpoints
-    float halfStrokeWidth = paint->getStrokeWidth() * 0.5f;
-    // A stroke width of 0 has a special meaning in Skia:
-    // it draws a line 1 px wide regardless of current transform
-    bool isHairLine = paint->getStrokeWidth() == 0.0f;
+    count &= ~0x3; // round down to nearest four
 
-    float inverseScaleX = 1.0f;
-    float inverseScaleY = 1.0f;
-    bool scaled = false;
+    VertexBuffer buffer;
+    SkRect bounds;
+    PathTessellator::tessellateLines(points, count, paint, mSnapshot->transform, bounds, buffer);
+    dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, *mSnapshot->transform);
 
-    int alpha;
-    SkXfermode::Mode mode;
-
-    int generatedVerticesCount = 0;
-    int verticesCount = count;
-    if (count > 4) {
-        // Polyline: account for extra vertices needed for continuous tri-strip
-        verticesCount += (count - 4);
-    }
-
-    if (isHairLine || isAA) {
-        // The quad that we use for AA and hairlines needs to account for scaling. For hairlines
-        // the line on the screen should always be one pixel wide regardless of scale. For
-        // AA lines, we only want one pixel of translucent boundary around the quad.
-        if (CC_UNLIKELY(!mSnapshot->transform->isPureTranslate())) {
-            Matrix4 *mat = mSnapshot->transform;
-            float m00 = mat->data[Matrix4::kScaleX];
-            float m01 = mat->data[Matrix4::kSkewY];
-            float m10 = mat->data[Matrix4::kSkewX];
-            float m11 = mat->data[Matrix4::kScaleY];
-
-            float scaleX = sqrtf(m00 * m00 + m01 * m01);
-            float scaleY = sqrtf(m10 * m10 + m11 * m11);
-
-            inverseScaleX = (scaleX != 0) ? (inverseScaleX / scaleX) : 0;
-            inverseScaleY = (scaleY != 0) ? (inverseScaleY / scaleY) : 0;
-
-            if (inverseScaleX != 1.0f || inverseScaleY != 1.0f) {
-                scaled = true;
-            }
-        }
-    }
-
-    getAlphaAndMode(paint, &alpha, &mode);
-
-    mCaches.enableScissor();
-
-    setupDraw();
-    setupDrawNoTexture();
-    if (isAA) {
-        setupDrawAA();
-    }
-    setupDrawColor(paint->getColor(), alpha);
-    setupDrawColorFilter();
-    setupDrawShader();
-    setupDrawBlending(isAA, mode);
-    setupDrawProgram();
-    setupDrawModelViewIdentity(true);
-    setupDrawColorUniforms();
-    setupDrawColorFilterUniforms();
-    setupDrawShaderIdentityUniforms();
-
-    if (isHairLine) {
-        // Set a real stroke width to be used in quad construction
-        halfStrokeWidth = isAA? 1 : .5;
-    } else if (isAA && !scaled) {
-        // Expand boundary to enable AA calculations on the quad border
-        halfStrokeWidth += .5f;
-    }
-
-    int widthSlot;
-    int lengthSlot;
-
-    Vertex lines[verticesCount];
-    Vertex* vertices = &lines[0];
-
-    AAVertex wLines[verticesCount];
-    AAVertex* aaVertices = &wLines[0];
-
-    if (CC_UNLIKELY(!isAA)) {
-        setupDrawVertices(vertices);
-    } else {
-        void* widthCoords = ((GLbyte*) aaVertices) + gVertexAAWidthOffset;
-        void* lengthCoords = ((GLbyte*) aaVertices) + gVertexAALengthOffset;
-        // innerProportion is the ratio of the inner (non-AA) part of the line to the total
-        // AA stroke width (the base stroke width expanded by a half pixel on either side).
-        // This value is used in the fragment shader to determine how to fill fragments.
-        // We will need to calculate the actual width proportion on each segment for
-        // scaled non-hairlines, since the boundary proportion may differ per-axis when scaled.
-        float boundaryWidthProportion = .5 - 1 / (2 * halfStrokeWidth);
-        setupDrawAALine((void*) aaVertices, widthCoords, lengthCoords,
-                boundaryWidthProportion, widthSlot, lengthSlot);
-    }
-
-    AAVertex* prevAAVertex = NULL;
-    Vertex* prevVertex = NULL;
-
-    int boundaryLengthSlot = -1;
-    int boundaryWidthSlot = -1;
-
-    for (int i = 0; i < count; i += 4) {
-        // a = start point, b = end point
-        vec2 a(points[i], points[i + 1]);
-        vec2 b(points[i + 2], points[i + 3]);
-
-        float length = 0;
-        float boundaryLengthProportion = 0;
-        float boundaryWidthProportion = 0;
-
-        // Find the normal to the line
-        vec2 n = (b - a).copyNormalized() * halfStrokeWidth;
-        float x = n.x;
-        n.x = -n.y;
-        n.y = x;
-
-        if (isHairLine) {
-            if (isAA) {
-                float wideningFactor;
-                if (fabs(n.x) >= fabs(n.y)) {
-                    wideningFactor = fabs(1.0f / n.x);
-                } else {
-                    wideningFactor = fabs(1.0f / n.y);
-                }
-                n *= wideningFactor;
-            }
-
-            if (scaled) {
-                n.x *= inverseScaleX;
-                n.y *= inverseScaleY;
-            }
-        } else if (scaled) {
-            // Extend n by .5 pixel on each side, post-transform
-            vec2 extendedN = n.copyNormalized();
-            extendedN /= 2;
-            extendedN.x *= inverseScaleX;
-            extendedN.y *= inverseScaleY;
-
-            float extendedNLength = extendedN.length();
-            // We need to set this value on the shader prior to drawing
-            boundaryWidthProportion = .5 - extendedNLength / (halfStrokeWidth + extendedNLength);
-            n += extendedN;
-        }
-
-        // aa lines expand the endpoint vertices to encompass the AA boundary
-        if (isAA) {
-            vec2 abVector = (b - a);
-            length = abVector.length();
-            abVector.normalize();
-
-            if (scaled) {
-                abVector.x *= inverseScaleX;
-                abVector.y *= inverseScaleY;
-                float abLength = abVector.length();
-                boundaryLengthProportion = .5 - abLength / (length + abLength);
-            } else {
-                boundaryLengthProportion = .5 - .5 / (length + 1);
-            }
-
-            abVector /= 2;
-            a -= abVector;
-            b += abVector;
-        }
-
-        // Four corners of the rectangle defining a thick line
-        vec2 p1 = a - n;
-        vec2 p2 = a + n;
-        vec2 p3 = b + n;
-        vec2 p4 = b - n;
-
-
-        const float left = fmin(p1.x, fmin(p2.x, fmin(p3.x, p4.x)));
-        const float right = fmax(p1.x, fmax(p2.x, fmax(p3.x, p4.x)));
-        const float top = fmin(p1.y, fmin(p2.y, fmin(p3.y, p4.y)));
-        const float bottom = fmax(p1.y, fmax(p2.y, fmax(p3.y, p4.y)));
-
-        if (!quickRejectNoScissor(left, top, right, bottom)) {
-            if (!isAA) {
-                if (prevVertex != NULL) {
-                    // Issue two repeat vertices to create degenerate triangles to bridge
-                    // between the previous line and the new one. This is necessary because
-                    // we are creating a single triangle_strip which will contain
-                    // potentially discontinuous line segments.
-                    Vertex::set(vertices++, prevVertex->position[0], prevVertex->position[1]);
-                    Vertex::set(vertices++, p1.x, p1.y);
-                    generatedVerticesCount += 2;
-                }
-
-                Vertex::set(vertices++, p1.x, p1.y);
-                Vertex::set(vertices++, p2.x, p2.y);
-                Vertex::set(vertices++, p4.x, p4.y);
-                Vertex::set(vertices++, p3.x, p3.y);
-
-                prevVertex = vertices - 1;
-                generatedVerticesCount += 4;
-            } else {
-                if (!isHairLine && scaled) {
-                    // Must set width proportions per-segment for scaled non-hairlines to use the
-                    // correct AA boundary dimensions
-                    if (boundaryWidthSlot < 0) {
-                        boundaryWidthSlot =
-                                mCaches.currentProgram->getUniform("boundaryWidth");
-                    }
-
-                    glUniform1f(boundaryWidthSlot, boundaryWidthProportion);
-                }
-
-                if (boundaryLengthSlot < 0) {
-                    boundaryLengthSlot = mCaches.currentProgram->getUniform("boundaryLength");
-                }
-
-                glUniform1f(boundaryLengthSlot, boundaryLengthProportion);
-
-                if (prevAAVertex != NULL) {
-                    // Issue two repeat vertices to create degenerate triangles to bridge
-                    // between the previous line and the new one. This is necessary because
-                    // we are creating a single triangle_strip which will contain
-                    // potentially discontinuous line segments.
-                    AAVertex::set(aaVertices++,prevAAVertex->position[0],
-                            prevAAVertex->position[1], prevAAVertex->width, prevAAVertex->length);
-                    AAVertex::set(aaVertices++, p4.x, p4.y, 1, 1);
-                    generatedVerticesCount += 2;
-                }
-
-                AAVertex::set(aaVertices++, p4.x, p4.y, 1, 1);
-                AAVertex::set(aaVertices++, p1.x, p1.y, 1, 0);
-                AAVertex::set(aaVertices++, p3.x, p3.y, 0, 1);
-                AAVertex::set(aaVertices++, p2.x, p2.y, 0, 0);
-
-                prevAAVertex = aaVertices - 1;
-                generatedVerticesCount += 4;
-            }
-
-            dirtyLayer(a.x == b.x ? left - 1 : left, a.y == b.y ? top - 1 : top,
-                    a.x == b.x ? right: right, a.y == b.y ? bottom: bottom,
-                    *mSnapshot->transform);
-        }
-    }
-
-    if (generatedVerticesCount > 0) {
-       glDrawArrays(GL_TRIANGLE_STRIP, 0, generatedVerticesCount);
-    }
-
-    if (isAA) {
-        finishDrawAALine(widthSlot, lengthSlot);
-    }
-
-    return DrawGlInfo::kStatusDrew;
+    bool useOffset = !paint->isAntiAlias();
+    return drawVertexBuffer(buffer, paint, useOffset);
 }
 
 status_t OpenGLRenderer::drawPoints(float* points, int count, SkPaint* paint) {
@@ -2540,9 +2261,7 @@
         ry += outset;
     }
     path.addRoundRect(rect, rx, ry);
-    drawConvexPath(path, p);
-
-    return DrawGlInfo::kStatusDrew;
+    return drawConvexPath(path, p);
 }
 
 status_t OpenGLRenderer::drawCircle(float x, float y, float radius, SkPaint* p) {
@@ -2562,9 +2281,7 @@
     } else {
         path.addCircle(x, y, radius);
     }
-    drawConvexPath(path, p);
-
-    return DrawGlInfo::kStatusDrew;
+    return drawConvexPath(path, p);
 }
 
 status_t OpenGLRenderer::drawOval(float left, float top, float right, float bottom,
@@ -2585,9 +2302,7 @@
         rect.outset(p->getStrokeWidth() / 2, p->getStrokeWidth() / 2);
     }
     path.addOval(rect);
-    drawConvexPath(path, p);
-
-    return DrawGlInfo::kStatusDrew;
+    return drawConvexPath(path, p);
 }
 
 status_t OpenGLRenderer::drawArc(float left, float top, float right, float bottom,
@@ -2601,8 +2316,7 @@
     }
 
     // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
-    if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != 0 ||
-            p->getStrokeCap() != SkPaint::kButt_Cap || useCenter) {
+    if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != 0 || useCenter) {
         mCaches.activeTexture(0);
         const PathTexture* texture = mCaches.arcShapeCache.getArc(right - left, bottom - top,
                 startAngle, sweepAngle, useCenter, p);
@@ -2622,9 +2336,7 @@
     if (useCenter) {
         path.close();
     }
-    drawConvexPath(path, p);
-
-    return DrawGlInfo::kStatusDrew;
+    return drawConvexPath(path, p);
 }
 
 // See SkPaintDefaults.h
@@ -2651,20 +2363,17 @@
             rect.outset(p->getStrokeWidth() / 2, p->getStrokeWidth() / 2);
         }
         path.addRect(rect);
-        drawConvexPath(path, p);
-
-        return DrawGlInfo::kStatusDrew;
+        return drawConvexPath(path, p);
     }
 
     if (p->isAntiAlias() && !mSnapshot->transform->isSimple()) {
         SkPath path;
         path.addRect(left, top, right, bottom);
-        drawConvexPath(path, p);
+        return drawConvexPath(path, p);
     } else {
         drawColorRect(left, top, right, bottom, p->getColor(), getXfermode(p->getXfermode()));
+        return DrawGlInfo::kStatusDrew;
     }
-
-    return DrawGlInfo::kStatusDrew;
 }
 
 void OpenGLRenderer::drawTextShadow(SkPaint* paint, const char* text, int bytesCount, int count,
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 750b3d2..ee205a4 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -53,6 +53,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 class DisplayList;
+class VertexBuffer;
 
 /**
  * OpenGL renderer used to draw accelerated 2D graphics. The API is a
@@ -595,12 +596,22 @@
     void drawAlphaBitmap(Texture* texture, float left, float top, SkPaint* paint);
 
     /**
+     * Renders a strip of polygons with the specified paint, used for tessellated geometry.
+     *
+     * @param vertexBuffer The VertexBuffer to be drawn
+     * @param paint The paint to render with
+     * @param useOffset Offset the vertexBuffer (used in drawing non-AA lines)
+     */
+    status_t drawVertexBuffer(const VertexBuffer& vertexBuffer, SkPaint* paint,
+            bool useOffset = false);
+
+    /**
      * Renders the convex hull defined by the specified path as a strip of polygons.
      *
      * @param path The hull of the path to draw
      * @param paint The paint to render with
      */
-    void drawConvexPath(const SkPath& path, SkPaint* paint);
+    status_t drawConvexPath(const SkPath& path, SkPaint* paint);
 
     /**
      * Draws a textured rectangle with the specified texture. The specified coordinates
@@ -767,7 +778,6 @@
     void setupDrawWithExternalTexture();
     void setupDrawNoTexture();
     void setupDrawAA();
-    void setupDrawVertexShape();
     void setupDrawPoint(float pointSize);
     void setupDrawColor(int color, int alpha);
     void setupDrawColor(float r, float g, float b, float a);
@@ -801,9 +811,6 @@
     void setupDrawMesh(GLvoid* vertices, GLvoid* texCoords = NULL, GLuint vbo = 0);
     void setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords);
     void setupDrawVertices(GLvoid* vertices);
-    void setupDrawAALine(GLvoid* vertices, GLvoid* distanceCoords, GLvoid* lengthCoords,
-            float strokeWidth, int& widthSlot, int& lengthSlot);
-    void finishDrawAALine(const int widthSlot, const int lengthSlot);
     void finishDrawTexture();
     void accountForClear(SkXfermode::Mode mode);
 
diff --git a/libs/hwui/PathRenderer.cpp b/libs/hwui/PathRenderer.cpp
deleted file mode 100644
index d59e36f..0000000
--- a/libs/hwui/PathRenderer.cpp
+++ /dev/null
@@ -1,720 +0,0 @@
-/*
- * 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.
- */
-
-#define LOG_TAG "PathRenderer"
-#define LOG_NDEBUG 1
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#define VERTEX_DEBUG 0
-
-#include <SkPath.h>
-#include <SkPaint.h>
-
-#include <stdlib.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Log.h>
-#include <utils/Trace.h>
-
-#include "PathRenderer.h"
-#include "Matrix.h"
-#include "Vector.h"
-#include "Vertex.h"
-
-namespace android {
-namespace uirenderer {
-
-#define THRESHOLD 0.5f
-
-SkRect PathRenderer::computePathBounds(const SkPath& path, const SkPaint* paint) {
-    SkRect bounds = path.getBounds();
-    if (paint->getStyle() != SkPaint::kFill_Style) {
-        float outset = paint->getStrokeWidth() * 0.5f;
-        bounds.outset(outset, outset);
-    }
-    return bounds;
-}
-
-void computeInverseScales(const mat4 *transform, float &inverseScaleX, float& inverseScaleY) {
-    if (CC_UNLIKELY(!transform->isPureTranslate())) {
-        float m00 = transform->data[Matrix4::kScaleX];
-        float m01 = transform->data[Matrix4::kSkewY];
-        float m10 = transform->data[Matrix4::kSkewX];
-        float m11 = transform->data[Matrix4::kScaleY];
-        float scaleX = sqrt(m00 * m00 + m01 * m01);
-        float scaleY = sqrt(m10 * m10 + m11 * m11);
-        inverseScaleX = (scaleX != 0) ? (1.0f / scaleX) : 1.0f;
-        inverseScaleY = (scaleY != 0) ? (1.0f / scaleY) : 1.0f;
-    } else {
-        inverseScaleX = 1.0f;
-        inverseScaleY = 1.0f;
-    }
-}
-
-inline void copyVertex(Vertex* destPtr, const Vertex* srcPtr) {
-    Vertex::set(destPtr, srcPtr->position[0], srcPtr->position[1]);
-}
-
-inline void copyAlphaVertex(AlphaVertex* destPtr, const AlphaVertex* srcPtr) {
-    AlphaVertex::set(destPtr, srcPtr->position[0], srcPtr->position[1], srcPtr->alpha);
-}
-
-/**
- * Produces a pseudo-normal for a vertex, given the normals of the two incoming lines. If the offset
- * from each vertex in a perimeter is calculated, the resultant lines connecting the offset vertices
- * will be offset by 1.0
- *
- * Note that we can't add and normalize the two vectors, that would result in a rectangle having an
- * offset of (sqrt(2)/2, sqrt(2)/2) at each corner, instead of (1, 1)
- *
- * NOTE: assumes angles between normals 90 degrees or less
- */
-inline vec2 totalOffsetFromNormals(const vec2& normalA, const vec2& normalB) {
-    return (normalA + normalB) / (1 + fabs(normalA.dot(normalB)));
-}
-
-inline void scaleOffsetForStrokeWidth(vec2& offset, float halfStrokeWidth,
-        float inverseScaleX, float inverseScaleY) {
-    if (halfStrokeWidth == 0.0f) {
-        // hairline - compensate for scale
-        offset.x *= 0.5f * inverseScaleX;
-        offset.y *= 0.5f * inverseScaleY;
-    } else {
-        offset *= halfStrokeWidth;
-    }
-}
-
-void getFillVerticesFromPerimeter(const Vector<Vertex>& perimeter, VertexBuffer& vertexBuffer) {
-    Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size());
-
-    int currentIndex = 0;
-    // zig zag between all previous points on the inside of the hull to create a
-    // triangle strip that fills the hull
-    int srcAindex = 0;
-    int srcBindex = perimeter.size() - 1;
-    while (srcAindex <= srcBindex) {
-        copyVertex(&buffer[currentIndex++], &perimeter[srcAindex]);
-        if (srcAindex == srcBindex) break;
-        copyVertex(&buffer[currentIndex++], &perimeter[srcBindex]);
-        srcAindex++;
-        srcBindex--;
-    }
-}
-
-void getStrokeVerticesFromPerimeter(const Vector<Vertex>& perimeter, float halfStrokeWidth,
-        VertexBuffer& vertexBuffer, float inverseScaleX, float inverseScaleY) {
-    Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size() * 2 + 2);
-
-    int currentIndex = 0;
-    const Vertex* last = &(perimeter[perimeter.size() - 1]);
-    const Vertex* current = &(perimeter[0]);
-    vec2 lastNormal(current->position[1] - last->position[1],
-            last->position[0] - current->position[0]);
-    lastNormal.normalize();
-    for (unsigned int i = 0; i < perimeter.size(); i++) {
-        const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
-        vec2 nextNormal(next->position[1] - current->position[1],
-                current->position[0] - next->position[0]);
-        nextNormal.normalize();
-
-        vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
-        scaleOffsetForStrokeWidth(totalOffset, halfStrokeWidth, inverseScaleX, inverseScaleY);
-
-        Vertex::set(&buffer[currentIndex++],
-                current->position[0] + totalOffset.x,
-                current->position[1] + totalOffset.y);
-
-        Vertex::set(&buffer[currentIndex++],
-                current->position[0] - totalOffset.x,
-                current->position[1] - totalOffset.y);
-
-        last = current;
-        current = next;
-        lastNormal = nextNormal;
-    }
-
-    // wrap around to beginning
-    copyVertex(&buffer[currentIndex++], &buffer[0]);
-    copyVertex(&buffer[currentIndex++], &buffer[1]);
-}
-
-void getStrokeVerticesFromUnclosedVertices(const Vector<Vertex>& vertices, float halfStrokeWidth,
-        VertexBuffer& vertexBuffer, float inverseScaleX, float inverseScaleY) {
-    Vertex* buffer = vertexBuffer.alloc<Vertex>(vertices.size() * 2);
-
-    int currentIndex = 0;
-    const Vertex* current = &(vertices[0]);
-    vec2 lastNormal;
-    for (unsigned int i = 0; i < vertices.size() - 1; i++) {
-        const Vertex* next = &(vertices[i + 1]);
-        vec2 nextNormal(next->position[1] - current->position[1],
-                current->position[0] - next->position[0]);
-        nextNormal.normalize();
-
-        vec2 totalOffset;
-        if (i == 0) {
-            totalOffset = nextNormal;
-        } else {
-            totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
-        }
-        scaleOffsetForStrokeWidth(totalOffset, halfStrokeWidth, inverseScaleX, inverseScaleY);
-
-        Vertex::set(&buffer[currentIndex++],
-                current->position[0] + totalOffset.x,
-                current->position[1] + totalOffset.y);
-
-        Vertex::set(&buffer[currentIndex++],
-                current->position[0] - totalOffset.x,
-                current->position[1] - totalOffset.y);
-
-        current = next;
-        lastNormal = nextNormal;
-    }
-
-    vec2 totalOffset = lastNormal;
-    scaleOffsetForStrokeWidth(totalOffset, halfStrokeWidth, inverseScaleX, inverseScaleY);
-
-    Vertex::set(&buffer[currentIndex++],
-            current->position[0] + totalOffset.x,
-            current->position[1] + totalOffset.y);
-    Vertex::set(&buffer[currentIndex++],
-            current->position[0] - totalOffset.x,
-            current->position[1] - totalOffset.y);
-#if VERTEX_DEBUG
-    for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) {
-        ALOGD("point at %f %f", buffer[i].position[0], buffer[i].position[1]);
-    }
-#endif
-}
-
-void getFillVerticesFromPerimeterAA(const Vector<Vertex>& perimeter, VertexBuffer& vertexBuffer,
-         float inverseScaleX, float inverseScaleY) {
-    AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(perimeter.size() * 3 + 2);
-
-    // generate alpha points - fill Alpha vertex gaps in between each point with
-    // alpha 0 vertex, offset by a scaled normal.
-    int currentIndex = 0;
-    const Vertex* last = &(perimeter[perimeter.size() - 1]);
-    const Vertex* current = &(perimeter[0]);
-    vec2 lastNormal(current->position[1] - last->position[1],
-            last->position[0] - current->position[0]);
-    lastNormal.normalize();
-    for (unsigned int i = 0; i < perimeter.size(); i++) {
-        const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
-        vec2 nextNormal(next->position[1] - current->position[1],
-                current->position[0] - next->position[0]);
-        nextNormal.normalize();
-
-        // AA point offset from original point is that point's normal, such that each side is offset
-        // by .5 pixels
-        vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
-        totalOffset.x *= 0.5f * inverseScaleX;
-        totalOffset.y *= 0.5f * inverseScaleY;
-
-        AlphaVertex::set(&buffer[currentIndex++],
-                current->position[0] + totalOffset.x,
-                current->position[1] + totalOffset.y,
-                0.0f);
-        AlphaVertex::set(&buffer[currentIndex++],
-                current->position[0] - totalOffset.x,
-                current->position[1] - totalOffset.y,
-                1.0f);
-
-        last = current;
-        current = next;
-        lastNormal = nextNormal;
-    }
-
-    // wrap around to beginning
-    copyAlphaVertex(&buffer[currentIndex++], &buffer[0]);
-    copyAlphaVertex(&buffer[currentIndex++], &buffer[1]);
-
-    // zig zag between all previous points on the inside of the hull to create a
-    // triangle strip that fills the hull, repeating the first inner point to
-    // create degenerate tris to start inside path
-    int srcAindex = 0;
-    int srcBindex = perimeter.size() - 1;
-    while (srcAindex <= srcBindex) {
-        copyAlphaVertex(&buffer[currentIndex++], &buffer[srcAindex * 2 + 1]);
-        if (srcAindex == srcBindex) break;
-        copyAlphaVertex(&buffer[currentIndex++], &buffer[srcBindex * 2 + 1]);
-        srcAindex++;
-        srcBindex--;
-    }
-
-#if VERTEX_DEBUG
-    for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) {
-        ALOGD("point at %f %f, alpha %f", buffer[i].position[0], buffer[i].position[1], buffer[i].alpha);
-    }
-#endif
-}
-
-
-void getStrokeVerticesFromUnclosedVerticesAA(const Vector<Vertex>& vertices, float halfStrokeWidth,
-        VertexBuffer& vertexBuffer, float inverseScaleX, float inverseScaleY) {
-    AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(6 * vertices.size() + 2);
-
-    // avoid lines smaller than hairline since they break triangle based sampling. instead reducing
-    // alpha value (TODO: support different X/Y scale)
-    float maxAlpha = 1.0f;
-    if (halfStrokeWidth != 0 && inverseScaleX == inverseScaleY &&
-            halfStrokeWidth * inverseScaleX < 0.5f) {
-        maxAlpha *= (2 * halfStrokeWidth) / inverseScaleX;
-        halfStrokeWidth = 0.0f;
-    }
-
-    // there is no outer/inner here, using them for consistency with below approach
-    int offset = 2 * (vertices.size() - 2);
-    int currentAAOuterIndex = 2;
-    int currentAAInnerIndex = 2 * offset + 5; // reversed
-    int currentStrokeIndex = currentAAInnerIndex + 7;
-
-    const Vertex* last = &(vertices[0]);
-    const Vertex* current = &(vertices[1]);
-    vec2 lastNormal(current->position[1] - last->position[1],
-            last->position[0] - current->position[0]);
-    lastNormal.normalize();
-
-    {
-        // start cap
-        vec2 totalOffset = lastNormal;
-        vec2 AAOffset = totalOffset;
-        AAOffset.x *= 0.5f * inverseScaleX;
-        AAOffset.y *= 0.5f * inverseScaleY;
-
-        vec2 innerOffset = totalOffset;
-        scaleOffsetForStrokeWidth(innerOffset, halfStrokeWidth, inverseScaleX, inverseScaleY);
-        vec2 outerOffset = innerOffset + AAOffset;
-        innerOffset -= AAOffset;
-
-        // TODO: support square cap by changing this offset to incorporate halfStrokeWidth
-        vec2 capAAOffset(AAOffset.y, -AAOffset.x);
-        AlphaVertex::set(&buffer[0],
-                last->position[0] + outerOffset.x + capAAOffset.x,
-                last->position[1] + outerOffset.y + capAAOffset.y,
-                0.0f);
-        AlphaVertex::set(&buffer[1],
-                last->position[0] + innerOffset.x - capAAOffset.x,
-                last->position[1] + innerOffset.y - capAAOffset.y,
-                maxAlpha);
-
-        AlphaVertex::set(&buffer[2 * offset + 6],
-                last->position[0] - outerOffset.x + capAAOffset.x,
-                last->position[1] - outerOffset.y + capAAOffset.y,
-                0.0f);
-        AlphaVertex::set(&buffer[2 * offset + 7],
-                last->position[0] - innerOffset.x - capAAOffset.x,
-                last->position[1] - innerOffset.y - capAAOffset.y,
-                maxAlpha);
-        copyAlphaVertex(&buffer[2 * offset + 8], &buffer[0]);
-        copyAlphaVertex(&buffer[2 * offset + 9], &buffer[1]);
-        copyAlphaVertex(&buffer[2 * offset + 10], &buffer[1]); // degenerate tris (the only two!)
-        copyAlphaVertex(&buffer[2 * offset + 11], &buffer[2 * offset + 7]);
-    }
-
-    for (unsigned int i = 1; i < vertices.size() - 1; i++) {
-        const Vertex* next = &(vertices[i + 1]);
-        vec2 nextNormal(next->position[1] - current->position[1],
-                current->position[0] - next->position[0]);
-        nextNormal.normalize();
-
-        vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
-        vec2 AAOffset = totalOffset;
-        AAOffset.x *= 0.5f * inverseScaleX;
-        AAOffset.y *= 0.5f * inverseScaleY;
-
-        vec2 innerOffset = totalOffset;
-        scaleOffsetForStrokeWidth(innerOffset, halfStrokeWidth, inverseScaleX, inverseScaleY);
-        vec2 outerOffset = innerOffset + AAOffset;
-        innerOffset -= AAOffset;
-
-        AlphaVertex::set(&buffer[currentAAOuterIndex++],
-                current->position[0] + outerOffset.x,
-                current->position[1] + outerOffset.y,
-                0.0f);
-        AlphaVertex::set(&buffer[currentAAOuterIndex++],
-                current->position[0] + innerOffset.x,
-                current->position[1] + innerOffset.y,
-                maxAlpha);
-
-        AlphaVertex::set(&buffer[currentStrokeIndex++],
-                current->position[0] + innerOffset.x,
-                current->position[1] + innerOffset.y,
-                maxAlpha);
-        AlphaVertex::set(&buffer[currentStrokeIndex++],
-                current->position[0] - innerOffset.x,
-                current->position[1] - innerOffset.y,
-                maxAlpha);
-
-        AlphaVertex::set(&buffer[currentAAInnerIndex--],
-                current->position[0] - innerOffset.x,
-                current->position[1] - innerOffset.y,
-                maxAlpha);
-        AlphaVertex::set(&buffer[currentAAInnerIndex--],
-                current->position[0] - outerOffset.x,
-                current->position[1] - outerOffset.y,
-                0.0f);
-
-        last = current;
-        current = next;
-        lastNormal = nextNormal;
-    }
-
-    {
-        // end cap
-        vec2 totalOffset = lastNormal;
-        vec2 AAOffset = totalOffset;
-        AAOffset.x *= 0.5f * inverseScaleX;
-        AAOffset.y *= 0.5f * inverseScaleY;
-
-        vec2 innerOffset = totalOffset;
-        scaleOffsetForStrokeWidth(innerOffset, halfStrokeWidth, inverseScaleX, inverseScaleY);
-        vec2 outerOffset = innerOffset + AAOffset;
-        innerOffset -= AAOffset;
-
-        // TODO: support square cap by changing this offset to incorporate halfStrokeWidth
-        vec2 capAAOffset(-AAOffset.y, AAOffset.x);
-
-        AlphaVertex::set(&buffer[offset + 2],
-                current->position[0] + outerOffset.x + capAAOffset.x,
-                current->position[1] + outerOffset.y + capAAOffset.y,
-                0.0f);
-        AlphaVertex::set(&buffer[offset + 3],
-                current->position[0] + innerOffset.x - capAAOffset.x,
-                current->position[1] + innerOffset.y - capAAOffset.y,
-                maxAlpha);
-
-        AlphaVertex::set(&buffer[offset + 4],
-                current->position[0] - outerOffset.x + capAAOffset.x,
-                current->position[1] - outerOffset.y + capAAOffset.y,
-                0.0f);
-        AlphaVertex::set(&buffer[offset + 5],
-                current->position[0] - innerOffset.x - capAAOffset.x,
-                current->position[1] - innerOffset.y - capAAOffset.y,
-                maxAlpha);
-
-        copyAlphaVertex(&buffer[vertexBuffer.getSize() - 2], &buffer[offset + 3]);
-        copyAlphaVertex(&buffer[vertexBuffer.getSize() - 1], &buffer[offset + 5]);
-    }
-
-#if VERTEX_DEBUG
-    for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) {
-        ALOGD("point at %f %f, alpha %f", buffer[i].position[0], buffer[i].position[1], buffer[i].alpha);
-    }
-#endif
-}
-
-
-void getStrokeVerticesFromPerimeterAA(const Vector<Vertex>& perimeter, float halfStrokeWidth,
-        VertexBuffer& vertexBuffer, float inverseScaleX, float inverseScaleY) {
-    AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(6 * perimeter.size() + 8);
-
-    // avoid lines smaller than hairline since they break triangle based sampling. instead reducing
-    // alpha value (TODO: support different X/Y scale)
-    float maxAlpha = 1.0f;
-    if (halfStrokeWidth != 0 && inverseScaleX == inverseScaleY &&
-            halfStrokeWidth * inverseScaleX < 0.5f) {
-        maxAlpha *= (2 * halfStrokeWidth) / inverseScaleX;
-        halfStrokeWidth = 0.0f;
-    }
-
-    int offset = 2 * perimeter.size() + 3;
-    int currentAAOuterIndex = 0;
-    int currentStrokeIndex = offset;
-    int currentAAInnerIndex = offset * 2;
-
-    const Vertex* last = &(perimeter[perimeter.size() - 1]);
-    const Vertex* current = &(perimeter[0]);
-    vec2 lastNormal(current->position[1] - last->position[1],
-            last->position[0] - current->position[0]);
-    lastNormal.normalize();
-    for (unsigned int i = 0; i < perimeter.size(); i++) {
-        const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
-        vec2 nextNormal(next->position[1] - current->position[1],
-                current->position[0] - next->position[0]);
-        nextNormal.normalize();
-
-        vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
-        vec2 AAOffset = totalOffset;
-        AAOffset.x *= 0.5f * inverseScaleX;
-        AAOffset.y *= 0.5f * inverseScaleY;
-
-        vec2 innerOffset = totalOffset;
-        scaleOffsetForStrokeWidth(innerOffset, halfStrokeWidth, inverseScaleX, inverseScaleY);
-        vec2 outerOffset = innerOffset + AAOffset;
-        innerOffset -= AAOffset;
-
-        AlphaVertex::set(&buffer[currentAAOuterIndex++],
-                current->position[0] + outerOffset.x,
-                current->position[1] + outerOffset.y,
-                0.0f);
-        AlphaVertex::set(&buffer[currentAAOuterIndex++],
-                current->position[0] + innerOffset.x,
-                current->position[1] + innerOffset.y,
-                maxAlpha);
-
-        AlphaVertex::set(&buffer[currentStrokeIndex++],
-                current->position[0] + innerOffset.x,
-                current->position[1] + innerOffset.y,
-                maxAlpha);
-        AlphaVertex::set(&buffer[currentStrokeIndex++],
-                current->position[0] - innerOffset.x,
-                current->position[1] - innerOffset.y,
-                maxAlpha);
-
-        AlphaVertex::set(&buffer[currentAAInnerIndex++],
-                current->position[0] - innerOffset.x,
-                current->position[1] - innerOffset.y,
-                maxAlpha);
-        AlphaVertex::set(&buffer[currentAAInnerIndex++],
-                current->position[0] - outerOffset.x,
-                current->position[1] - outerOffset.y,
-                0.0f);
-
-        last = current;
-        current = next;
-        lastNormal = nextNormal;
-    }
-
-    // wrap each strip around to beginning, creating degenerate tris to bridge strips
-    copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[0]);
-    copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[1]);
-    copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[1]);
-
-    copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset]);
-    copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset + 1]);
-    copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset + 1]);
-
-    copyAlphaVertex(&buffer[currentAAInnerIndex++], &buffer[2 * offset]);
-    copyAlphaVertex(&buffer[currentAAInnerIndex++], &buffer[2 * offset + 1]);
-    // don't need to create last degenerate tri
-
-#if VERTEX_DEBUG
-    for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) {
-        ALOGD("point at %f %f, alpha %f", buffer[i].position[0], buffer[i].position[1], buffer[i].alpha);
-    }
-#endif
-}
-
-void PathRenderer::convexPathVertices(const SkPath &path, const SkPaint* paint,
-        const mat4 *transform, VertexBuffer& vertexBuffer) {
-    ATRACE_CALL();
-
-    SkPaint::Style style = paint->getStyle();
-    bool isAA = paint->isAntiAlias();
-
-    float inverseScaleX, inverseScaleY;
-    computeInverseScales(transform, inverseScaleX, inverseScaleY);
-
-    Vector<Vertex> tempVertices;
-    float threshInvScaleX = inverseScaleX;
-    float threshInvScaleY = inverseScaleY;
-    if (style == SkPaint::kStroke_Style) {
-        // alter the bezier recursion threshold values we calculate in order to compensate for
-        // expansion done after the path vertices are found
-        SkRect bounds = path.getBounds();
-        if (!bounds.isEmpty()) {
-            threshInvScaleX *= bounds.width() / (bounds.width() + paint->getStrokeWidth());
-            threshInvScaleY *= bounds.height() / (bounds.height() + paint->getStrokeWidth());
-        }
-    }
-
-    // force close if we're filling the path, since fill path expects closed perimeter.
-    bool forceClose = style != SkPaint::kStroke_Style;
-    bool wasClosed = convexPathPerimeterVertices(path, forceClose, threshInvScaleX * threshInvScaleX,
-            threshInvScaleY * threshInvScaleY, tempVertices);
-
-    if (!tempVertices.size()) {
-        // path was empty, return without allocating vertex buffer
-        return;
-    }
-
-#if VERTEX_DEBUG
-    for (unsigned int i = 0; i < tempVertices.size(); i++) {
-        ALOGD("orig path: point at %f %f", tempVertices[i].position[0], tempVertices[i].position[1]);
-    }
-#endif
-
-    if (style == SkPaint::kStroke_Style) {
-        float halfStrokeWidth = paint->getStrokeWidth() * 0.5f;
-        if (!isAA) {
-            if (wasClosed) {
-                getStrokeVerticesFromPerimeter(tempVertices, halfStrokeWidth, vertexBuffer,
-                        inverseScaleX, inverseScaleY);
-            } else {
-                getStrokeVerticesFromUnclosedVertices(tempVertices, halfStrokeWidth, vertexBuffer,
-                        inverseScaleX, inverseScaleY);
-            }
-
-        } else {
-            if (wasClosed) {
-                getStrokeVerticesFromPerimeterAA(tempVertices, halfStrokeWidth, vertexBuffer,
-                        inverseScaleX, inverseScaleY);
-            } else {
-                getStrokeVerticesFromUnclosedVerticesAA(tempVertices, halfStrokeWidth, vertexBuffer,
-                        inverseScaleX, inverseScaleY);
-            }
-        }
-    } else {
-        // For kStrokeAndFill style, the path should be adjusted externally, as it will be treated as a fill here.
-        if (!isAA) {
-            getFillVerticesFromPerimeter(tempVertices, vertexBuffer);
-        } else {
-            getFillVerticesFromPerimeterAA(tempVertices, vertexBuffer, inverseScaleX, inverseScaleY);
-        }
-    }
-}
-
-
-void pushToVector(Vector<Vertex>& vertices, float x, float y) {
-    // TODO: make this not yuck
-    vertices.push();
-    Vertex* newVertex = &(vertices.editArray()[vertices.size() - 1]);
-    Vertex::set(newVertex, x, y);
-}
-
-bool PathRenderer::convexPathPerimeterVertices(const SkPath& path, bool forceClose,
-        float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) {
-    ATRACE_CALL();
-
-    // TODO: to support joins other than sharp miter, join vertices should be labelled in the
-    // perimeter, or resolved into more vertices. Reconsider forceClose-ing in that case.
-    SkPath::Iter iter(path, forceClose);
-    SkPoint pts[4];
-    SkPath::Verb v;
-    while (SkPath::kDone_Verb != (v = iter.next(pts))) {
-            switch (v) {
-                case SkPath::kMove_Verb:
-                    pushToVector(outputVertices, pts[0].x(), pts[0].y());
-                    ALOGV("Move to pos %f %f", pts[0].x(), pts[0].y());
-                    break;
-                case SkPath::kClose_Verb:
-                    ALOGV("Close at pos %f %f", pts[0].x(), pts[0].y());
-                    break;
-                case SkPath::kLine_Verb:
-                    ALOGV("kLine_Verb %f %f -> %f %f",
-                            pts[0].x(), pts[0].y(),
-                            pts[1].x(), pts[1].y());
-
-                    pushToVector(outputVertices, pts[1].x(), pts[1].y());
-                    break;
-                case SkPath::kQuad_Verb:
-                    ALOGV("kQuad_Verb");
-                    recursiveQuadraticBezierVertices(
-                            pts[0].x(), pts[0].y(),
-                            pts[2].x(), pts[2].y(),
-                            pts[1].x(), pts[1].y(),
-                            sqrInvScaleX, sqrInvScaleY, outputVertices);
-                    break;
-                case SkPath::kCubic_Verb:
-                    ALOGV("kCubic_Verb");
-                    recursiveCubicBezierVertices(
-                            pts[0].x(), pts[0].y(),
-                            pts[1].x(), pts[1].y(),
-                            pts[3].x(), pts[3].y(),
-                            pts[2].x(), pts[2].y(),
-                        sqrInvScaleX, sqrInvScaleY, outputVertices);
-                    break;
-                default:
-                    break;
-            }
-    }
-
-    int size = outputVertices.size();
-    if (size >= 2 && outputVertices[0].position[0] == outputVertices[size - 1].position[0] &&
-            outputVertices[0].position[1] == outputVertices[size - 1].position[1]) {
-        outputVertices.pop();
-        return true;
-    }
-    return false;
-}
-
-void PathRenderer::recursiveCubicBezierVertices(
-        float p1x, float p1y, float c1x, float c1y,
-        float p2x, float p2y, float c2x, float c2y,
-        float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) {
-    float dx = p2x - p1x;
-    float dy = p2y - p1y;
-    float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx);
-    float d2 = fabs((c2x - p2x) * dy - (c2y - p2y) * dx);
-    float d = d1 + d2;
-
-    // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
-
-    if (d * d < THRESHOLD * THRESHOLD * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
-        // below thresh, draw line by adding endpoint
-        pushToVector(outputVertices, p2x, p2y);
-    } else {
-        float p1c1x = (p1x + c1x) * 0.5f;
-        float p1c1y = (p1y + c1y) * 0.5f;
-        float p2c2x = (p2x + c2x) * 0.5f;
-        float p2c2y = (p2y + c2y) * 0.5f;
-
-        float c1c2x = (c1x + c2x) * 0.5f;
-        float c1c2y = (c1y + c2y) * 0.5f;
-
-        float p1c1c2x = (p1c1x + c1c2x) * 0.5f;
-        float p1c1c2y = (p1c1y + c1c2y) * 0.5f;
-
-        float p2c1c2x = (p2c2x + c1c2x) * 0.5f;
-        float p2c1c2y = (p2c2y + c1c2y) * 0.5f;
-
-        float mx = (p1c1c2x + p2c1c2x) * 0.5f;
-        float my = (p1c1c2y + p2c1c2y) * 0.5f;
-
-        recursiveCubicBezierVertices(
-                p1x, p1y, p1c1x, p1c1y,
-                mx, my, p1c1c2x, p1c1c2y,
-                sqrInvScaleX, sqrInvScaleY, outputVertices);
-        recursiveCubicBezierVertices(
-                mx, my, p2c1c2x, p2c1c2y,
-                p2x, p2y, p2c2x, p2c2y,
-                sqrInvScaleX, sqrInvScaleY, outputVertices);
-    }
-}
-
-void PathRenderer::recursiveQuadraticBezierVertices(
-        float ax, float ay,
-        float bx, float by,
-        float cx, float cy,
-        float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) {
-    float dx = bx - ax;
-    float dy = by - ay;
-    float d = (cx - bx) * dy - (cy - by) * dx;
-
-    if (d * d < THRESHOLD * THRESHOLD * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
-        // below thresh, draw line by adding endpoint
-        pushToVector(outputVertices, bx, by);
-    } else {
-        float acx = (ax + cx) * 0.5f;
-        float bcx = (bx + cx) * 0.5f;
-        float acy = (ay + cy) * 0.5f;
-        float bcy = (by + cy) * 0.5f;
-
-        // midpoint
-        float mx = (acx + bcx) * 0.5f;
-        float my = (acy + bcy) * 0.5f;
-
-        recursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy,
-                sqrInvScaleX, sqrInvScaleY, outputVertices);
-        recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy,
-                sqrInvScaleX, sqrInvScaleY, outputVertices);
-    }
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/PathRenderer.h b/libs/hwui/PathRenderer.h
deleted file mode 100644
index e9f347b..0000000
--- a/libs/hwui/PathRenderer.h
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ANDROID_HWUI_PATH_RENDERER_H
-#define ANDROID_HWUI_PATH_RENDERER_H
-
-#include <utils/Vector.h>
-
-#include "Vertex.h"
-
-namespace android {
-namespace uirenderer {
-
-class Matrix4;
-typedef Matrix4 mat4;
-
-class VertexBuffer {
-public:
-    VertexBuffer():
-        mBuffer(0),
-        mSize(0),
-        mCleanupMethod(0)
-    {}
-
-    ~VertexBuffer() {
-        if (mCleanupMethod)
-            mCleanupMethod(mBuffer);
-    }
-
-    template <class TYPE>
-    TYPE* alloc(int size) {
-        mSize = size;
-        mBuffer = (void*)new TYPE[size];
-        mCleanupMethod = &(cleanup<TYPE>);
-
-        return (TYPE*)mBuffer;
-    }
-
-    void* getBuffer() { return mBuffer; }
-    unsigned int getSize() { return mSize; }
-
-private:
-    template <class TYPE>
-    static void cleanup(void* buffer) {
-        delete[] (TYPE*)buffer;
-    }
-
-    void* mBuffer;
-    unsigned int mSize;
-    void (*mCleanupMethod)(void*);
-};
-
-class PathRenderer {
-public:
-    static SkRect computePathBounds(const SkPath& path, const SkPaint* paint);
-
-    static void convexPathVertices(const SkPath& path, const SkPaint* paint,
-            const mat4 *transform, VertexBuffer& vertexBuffer);
-
-private:
-    static bool convexPathPerimeterVertices(const SkPath &path, bool forceClose,
-        float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex> &outputVertices);
-
-/*
-  endpoints a & b,
-  control c
- */
-    static void recursiveQuadraticBezierVertices(
-            float ax, float ay,
-            float bx, float by,
-            float cx, float cy,
-            float sqrInvScaleX, float sqrInvScaleY,
-            Vector<Vertex> &outputVertices);
-
-/*
-  endpoints p1, p2
-  control c1, c2
- */
-    static void recursiveCubicBezierVertices(
-            float p1x, float p1y,
-            float c1x, float c1y,
-            float p2x, float p2y,
-            float c2x, float c2y,
-            float sqrInvScaleX, float sqrInvScaleY,
-            Vector<Vertex> &outputVertices);
-};
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_PATH_RENDERER_H
diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp
new file mode 100644
index 0000000..69ccdfa
--- /dev/null
+++ b/libs/hwui/PathTessellator.cpp
@@ -0,0 +1,970 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "PathTessellator"
+#define LOG_NDEBUG 1
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#define VERTEX_DEBUG 0
+
+#if VERTEX_DEBUG
+#define DEBUG_DUMP_ALPHA_BUFFER() \
+    for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \
+        ALOGD("point %d at %f %f, alpha %f", \
+        i, buffer[i].position[0], buffer[i].position[1], buffer[i].alpha); \
+    }
+#define DEBUG_DUMP_BUFFER() \
+    for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \
+        ALOGD("point %d at %f %f", i, buffer[i].position[0], buffer[i].position[1]); \
+    }
+#else
+#define DEBUG_DUMP_ALPHA_BUFFER()
+#define DEBUG_DUMP_BUFFER()
+#endif
+
+#include <SkPath.h>
+#include <SkPaint.h>
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Log.h>
+#include <utils/Trace.h>
+
+#include "PathTessellator.h"
+#include "Matrix.h"
+#include "Vector.h"
+#include "Vertex.h"
+
+namespace android {
+namespace uirenderer {
+
+#define THRESHOLD 0.5f
+#define ROUND_CAP_THRESH 0.25f
+#define PI 3.1415926535897932f
+
+void PathTessellator::expandBoundsForStroke(SkRect& bounds, const SkPaint* paint,
+        bool forceExpand) {
+    if (forceExpand || paint->getStyle() != SkPaint::kFill_Style) {
+        float outset = paint->getStrokeWidth() * 0.5f;
+        bounds.outset(outset, outset);
+    }
+}
+
+inline void copyVertex(Vertex* destPtr, const Vertex* srcPtr) {
+    Vertex::set(destPtr, srcPtr->position[0], srcPtr->position[1]);
+}
+
+inline void copyAlphaVertex(AlphaVertex* destPtr, const AlphaVertex* srcPtr) {
+    AlphaVertex::set(destPtr, srcPtr->position[0], srcPtr->position[1], srcPtr->alpha);
+}
+
+/**
+ * Produces a pseudo-normal for a vertex, given the normals of the two incoming lines. If the offset
+ * from each vertex in a perimeter is calculated, the resultant lines connecting the offset vertices
+ * will be offset by 1.0
+ *
+ * Note that we can't add and normalize the two vectors, that would result in a rectangle having an
+ * offset of (sqrt(2)/2, sqrt(2)/2) at each corner, instead of (1, 1)
+ *
+ * NOTE: assumes angles between normals 90 degrees or less
+ */
+inline vec2 totalOffsetFromNormals(const vec2& normalA, const vec2& normalB) {
+    return (normalA + normalB) / (1 + fabs(normalA.dot(normalB)));
+}
+
+/**
+ * Structure used for storing useful information about the SkPaint and scale used for tessellating
+ */
+struct PaintInfo {
+public:
+    PaintInfo(const SkPaint* paint, const mat4 *transform) :
+            style(paint->getStyle()), cap(paint->getStrokeCap()), isAA(paint->isAntiAlias()),
+            inverseScaleX(1.0f), inverseScaleY(1.0f),
+            halfStrokeWidth(paint->getStrokeWidth() * 0.5f), maxAlpha(1.0f) {
+        // compute inverse scales
+        if (CC_UNLIKELY(!transform->isPureTranslate())) {
+            float m00 = transform->data[Matrix4::kScaleX];
+            float m01 = transform->data[Matrix4::kSkewY];
+            float m10 = transform->data[Matrix4::kSkewX];
+            float m11 = transform->data[Matrix4::kScaleY];
+            float scaleX = sqrt(m00 * m00 + m01 * m01);
+            float scaleY = sqrt(m10 * m10 + m11 * m11);
+            inverseScaleX = (scaleX != 0) ? (1.0f / scaleX) : 1.0f;
+            inverseScaleY = (scaleY != 0) ? (1.0f / scaleY) : 1.0f;
+        }
+
+        if (isAA && halfStrokeWidth != 0 && inverseScaleX == inverseScaleY &&
+                halfStrokeWidth * inverseScaleX < 0.5f) {
+            maxAlpha *= (2 * halfStrokeWidth) / inverseScaleX;
+            halfStrokeWidth = 0.0f;
+        }
+    }
+
+    SkPaint::Style style;
+    SkPaint::Cap cap;
+    bool isAA;
+    float inverseScaleX;
+    float inverseScaleY;
+    float halfStrokeWidth;
+    float maxAlpha;
+
+    inline void scaleOffsetForStrokeWidth(vec2& offset) const {
+        if (halfStrokeWidth == 0.0f) {
+            // hairline - compensate for scale
+            offset.x *= 0.5f * inverseScaleX;
+            offset.y *= 0.5f * inverseScaleY;
+        } else {
+            offset *= halfStrokeWidth;
+        }
+    }
+
+    /**
+     * NOTE: the input will not always be a normal, especially for sharp edges - it should be the
+     * result of totalOffsetFromNormals (see documentation there)
+     */
+    inline vec2 deriveAAOffset(const vec2& offset) const {
+        return vec2(offset.x * 0.5f * inverseScaleX,
+            offset.y * 0.5f * inverseScaleY);
+    }
+
+    /**
+     * Returns the number of cap divisions beyond the minimum 2 (kButt_Cap/kSquareCap will return 0)
+     * Should only be used when stroking and drawing caps
+     */
+    inline int capExtraDivisions() const {
+        if (cap == SkPaint::kRound_Cap) {
+            if (halfStrokeWidth == 0.0f) return 2;
+
+            // ROUND_CAP_THRESH is the maximum error for polygonal approximation of the round cap
+            const float errConst = (-ROUND_CAP_THRESH / halfStrokeWidth + 1);
+            const float targetCosVal = 2 * errConst * errConst - 1;
+            int neededDivisions = (int)(ceilf(PI / acos(targetCosVal)/2)) * 2;
+            return neededDivisions;
+        }
+        return 0;
+    }
+};
+
+void getFillVerticesFromPerimeter(const Vector<Vertex>& perimeter, VertexBuffer& vertexBuffer) {
+    Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size());
+
+    int currentIndex = 0;
+    // zig zag between all previous points on the inside of the hull to create a
+    // triangle strip that fills the hull
+    int srcAindex = 0;
+    int srcBindex = perimeter.size() - 1;
+    while (srcAindex <= srcBindex) {
+        copyVertex(&buffer[currentIndex++], &perimeter[srcAindex]);
+        if (srcAindex == srcBindex) break;
+        copyVertex(&buffer[currentIndex++], &perimeter[srcBindex]);
+        srcAindex++;
+        srcBindex--;
+    }
+}
+
+/*
+ * Fills a vertexBuffer with non-alpha vertices, zig-zagging at each perimeter point to create a
+ * tri-strip as wide as the stroke.
+ *
+ * Uses an additional 2 vertices at the end to wrap around, closing the tri-strip
+ * (for a total of perimeter.size() * 2 + 2 vertices)
+ */
+void getStrokeVerticesFromPerimeter(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter,
+        VertexBuffer& vertexBuffer) {
+    Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size() * 2 + 2);
+
+    int currentIndex = 0;
+    const Vertex* last = &(perimeter[perimeter.size() - 1]);
+    const Vertex* current = &(perimeter[0]);
+    vec2 lastNormal(current->position[1] - last->position[1],
+            last->position[0] - current->position[0]);
+    lastNormal.normalize();
+    for (unsigned int i = 0; i < perimeter.size(); i++) {
+        const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
+        vec2 nextNormal(next->position[1] - current->position[1],
+                current->position[0] - next->position[0]);
+        nextNormal.normalize();
+
+        vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
+        paintInfo.scaleOffsetForStrokeWidth(totalOffset);
+
+        Vertex::set(&buffer[currentIndex++],
+                current->position[0] + totalOffset.x,
+                current->position[1] + totalOffset.y);
+
+        Vertex::set(&buffer[currentIndex++],
+                current->position[0] - totalOffset.x,
+                current->position[1] - totalOffset.y);
+
+        last = current;
+        current = next;
+        lastNormal = nextNormal;
+    }
+
+    // wrap around to beginning
+    copyVertex(&buffer[currentIndex++], &buffer[0]);
+    copyVertex(&buffer[currentIndex++], &buffer[1]);
+
+    DEBUG_DUMP_BUFFER();
+}
+
+/**
+ * Fills a vertexBuffer with non-alpha vertices similar to getStrokeVerticesFromPerimeter, except:
+ *
+ * 1 - Doesn't need to wrap around, since the input vertices are unclosed
+ *
+ * 2 - can zig-zag across 'extra' vertices at either end, to create round caps
+ */
+void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo,
+        const Vector<Vertex>& vertices, VertexBuffer& vertexBuffer) {
+    const int extra = paintInfo.capExtraDivisions();
+    const int allocSize = (vertices.size() + extra) * 2;
+
+    Vertex* buffer = vertexBuffer.alloc<Vertex>(allocSize);
+
+    if (extra > 0) {
+        // tessellate both round caps
+        const int last = vertices.size() - 1;
+        float beginTheta = atan2(
+                - (vertices[0].position[0] - vertices[1].position[0]),
+                vertices[0].position[1] - vertices[1].position[1]);
+        float endTheta = atan2(
+                - (vertices[last].position[0] - vertices[last - 1].position[0]),
+                vertices[last].position[1] - vertices[last - 1].position[1]);
+
+        const float dTheta = PI / (extra + 1);
+        const float radialScale = 2.0f / (1 + cos(dTheta));
+
+        int capOffset;
+        for (int i = 0; i < extra; i++) {
+            if (i < extra / 2) {
+                capOffset = extra - 2 * i - 1;
+            } else {
+                capOffset = 2 * i - extra;
+            }
+
+            beginTheta += dTheta;
+            vec2 beginRadialOffset(cos(beginTheta), sin(beginTheta));
+            paintInfo.scaleOffsetForStrokeWidth(beginRadialOffset);
+            Vertex::set(&buffer[capOffset],
+                    vertices[0].position[0] + beginRadialOffset.x,
+                    vertices[0].position[1] + beginRadialOffset.y);
+
+            endTheta += dTheta;
+            vec2 endRadialOffset(cos(endTheta), sin(endTheta));
+            paintInfo.scaleOffsetForStrokeWidth(endRadialOffset);
+            Vertex::set(&buffer[allocSize - 1 - capOffset],
+                    vertices[last].position[0] + endRadialOffset.x,
+                    vertices[last].position[1] + endRadialOffset.y);
+        }
+    }
+
+    int currentIndex = extra;
+    const Vertex* current = &(vertices[0]);
+    vec2 lastNormal;
+    for (unsigned int i = 0; i < vertices.size() - 1; i++) {
+        const Vertex* next = &(vertices[i + 1]);
+        vec2 nextNormal(next->position[1] - current->position[1],
+                current->position[0] - next->position[0]);
+        nextNormal.normalize();
+
+        vec2 totalOffset;
+        if (i == 0) {
+            totalOffset = nextNormal;
+        } else {
+            totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
+        }
+        paintInfo.scaleOffsetForStrokeWidth(totalOffset);
+
+        Vertex::set(&buffer[currentIndex++],
+                current->position[0] + totalOffset.x,
+                current->position[1] + totalOffset.y);
+
+        Vertex::set(&buffer[currentIndex++],
+                current->position[0] - totalOffset.x,
+                current->position[1] - totalOffset.y);
+
+        current = next;
+        lastNormal = nextNormal;
+    }
+
+    vec2 totalOffset = lastNormal;
+    paintInfo.scaleOffsetForStrokeWidth(totalOffset);
+
+    Vertex::set(&buffer[currentIndex++],
+            current->position[0] + totalOffset.x,
+            current->position[1] + totalOffset.y);
+    Vertex::set(&buffer[currentIndex++],
+            current->position[0] - totalOffset.x,
+            current->position[1] - totalOffset.y);
+
+    DEBUG_DUMP_BUFFER();
+}
+
+/**
+ * Populates a vertexBuffer with AlphaVertices to create an anti-aliased fill shape tessellation
+ * 
+ * 1 - create the AA perimeter of unit width, by zig-zagging at each point around the perimeter of
+ * the shape (using 2 * perimeter.size() vertices)
+ *
+ * 2 - wrap around to the beginning to complete the perimeter (2 vertices)
+ *
+ * 3 - zig zag back and forth inside the shape to fill it (using perimeter.size() vertices)
+ */
+void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter,
+        VertexBuffer& vertexBuffer) {
+    AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(perimeter.size() * 3 + 2);
+
+    // generate alpha points - fill Alpha vertex gaps in between each point with
+    // alpha 0 vertex, offset by a scaled normal.
+    int currentIndex = 0;
+    const Vertex* last = &(perimeter[perimeter.size() - 1]);
+    const Vertex* current = &(perimeter[0]);
+    vec2 lastNormal(current->position[1] - last->position[1],
+            last->position[0] - current->position[0]);
+    lastNormal.normalize();
+    for (unsigned int i = 0; i < perimeter.size(); i++) {
+        const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
+        vec2 nextNormal(next->position[1] - current->position[1],
+                current->position[0] - next->position[0]);
+        nextNormal.normalize();
+
+        // AA point offset from original point is that point's normal, such that each side is offset
+        // by .5 pixels
+        vec2 totalOffset = paintInfo.deriveAAOffset(totalOffsetFromNormals(lastNormal, nextNormal));
+
+        AlphaVertex::set(&buffer[currentIndex++],
+                current->position[0] + totalOffset.x,
+                current->position[1] + totalOffset.y,
+                0.0f);
+        AlphaVertex::set(&buffer[currentIndex++],
+                current->position[0] - totalOffset.x,
+                current->position[1] - totalOffset.y,
+                1.0f);
+
+        last = current;
+        current = next;
+        lastNormal = nextNormal;
+    }
+
+    // wrap around to beginning
+    copyAlphaVertex(&buffer[currentIndex++], &buffer[0]);
+    copyAlphaVertex(&buffer[currentIndex++], &buffer[1]);
+
+    // zig zag between all previous points on the inside of the hull to create a
+    // triangle strip that fills the hull, repeating the first inner point to
+    // create degenerate tris to start inside path
+    int srcAindex = 0;
+    int srcBindex = perimeter.size() - 1;
+    while (srcAindex <= srcBindex) {
+        copyAlphaVertex(&buffer[currentIndex++], &buffer[srcAindex * 2 + 1]);
+        if (srcAindex == srcBindex) break;
+        copyAlphaVertex(&buffer[currentIndex++], &buffer[srcBindex * 2 + 1]);
+        srcAindex++;
+        srcBindex--;
+    }
+
+    DEBUG_DUMP_BUFFER();
+}
+
+/**
+ * Stores geometry for a single, AA-perimeter (potentially rounded) cap
+ *
+ * For explanation of constants and general methodoloyg, see comments for
+ * getStrokeVerticesFromUnclosedVerticesAA() below.
+ */
+inline void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>& vertices,
+        AlphaVertex* buffer, bool isFirst, vec2 normal, int offset) {
+    const int extra = paintInfo.capExtraDivisions();
+    const int extraOffset = (extra + 1) / 2;
+    const int capIndex = isFirst
+            ? 2 * offset + 6 + 2 * (extra + extraOffset)
+            : offset + 2 + 2 * extraOffset;
+    if (isFirst) normal *= -1;
+
+    // TODO: this normal should be scaled by radialScale if extra != 0, see totalOffsetFromNormals()
+    vec2 AAOffset = paintInfo.deriveAAOffset(normal);
+
+    vec2 strokeOffset = normal;
+    paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
+    vec2 outerOffset = strokeOffset + AAOffset;
+    vec2 innerOffset = strokeOffset - AAOffset;
+
+    vec2 capAAOffset;
+    if (paintInfo.cap != SkPaint::kRound_Cap) {
+        // if the cap is square or butt, the inside primary cap vertices will be inset in two
+        // directions - both normal to the stroke, and parallel to it.
+        capAAOffset = vec2(-AAOffset.y, AAOffset.x);
+    }
+
+    // determine referencePoint, the center point for the 4 primary cap vertices
+    const Vertex* point = isFirst ? vertices.begin() : (vertices.end() - 1);
+    vec2 referencePoint(point->position[0], point->position[1]);
+    if (paintInfo.cap == SkPaint::kSquare_Cap) {
+        // To account for square cap, move the primary cap vertices (that create the AA edge) by the
+        // stroke offset vector (rotated to be parallel to the stroke)
+        referencePoint += vec2(-strokeOffset.y, strokeOffset.x);
+    }
+
+    AlphaVertex::set(&buffer[capIndex + 0],
+            referencePoint.x + outerOffset.x + capAAOffset.x,
+            referencePoint.y + outerOffset.y + capAAOffset.y,
+            0.0f);
+    AlphaVertex::set(&buffer[capIndex + 1],
+            referencePoint.x + innerOffset.x - capAAOffset.x,
+            referencePoint.y + innerOffset.y - capAAOffset.y,
+            paintInfo.maxAlpha);
+
+    bool isRound = paintInfo.cap == SkPaint::kRound_Cap;
+
+    const int postCapIndex = (isRound && isFirst) ? (2 * extraOffset - 2) : capIndex + (2 * extra);
+    AlphaVertex::set(&buffer[postCapIndex + 2],
+            referencePoint.x - outerOffset.x + capAAOffset.x,
+            referencePoint.y - outerOffset.y + capAAOffset.y,
+            0.0f);
+    AlphaVertex::set(&buffer[postCapIndex + 3],
+            referencePoint.x - innerOffset.x - capAAOffset.x,
+            referencePoint.y - innerOffset.y - capAAOffset.y,
+            paintInfo.maxAlpha);
+
+    if (isRound) {
+        const float dTheta = PI / (extra + 1);
+        const float radialScale = 2.0f / (1 + cos(dTheta));
+        float theta = atan2(normal.y, normal.x);
+        int capPerimIndex = capIndex + 2;
+
+        for (int i = 0; i < extra; i++) {
+            theta += dTheta;
+
+            vec2 radialOffset(cos(theta), sin(theta));
+
+            // scale to compensate for pinching at sharp angles, see totalOffsetFromNormals()
+            radialOffset *= radialScale;
+
+            AAOffset = paintInfo.deriveAAOffset(radialOffset);
+            paintInfo.scaleOffsetForStrokeWidth(radialOffset);
+            AlphaVertex::set(&buffer[capPerimIndex++],
+                    referencePoint.x + radialOffset.x + AAOffset.x,
+                    referencePoint.y + radialOffset.y + AAOffset.y,
+                    0.0f);
+            AlphaVertex::set(&buffer[capPerimIndex++],
+                    referencePoint.x + radialOffset.x - AAOffset.x,
+                    referencePoint.y + radialOffset.y - AAOffset.y,
+                    paintInfo.maxAlpha);
+
+            if (isFirst && i == extra - extraOffset) {
+                //copy most recent two points to first two points
+                copyAlphaVertex(&buffer[0], &buffer[capPerimIndex - 2]);
+                copyAlphaVertex(&buffer[1], &buffer[capPerimIndex - 1]);
+
+                capPerimIndex = 2; // start writing the rest of the round cap at index 2
+            }
+        }
+
+        if (isFirst) {
+            const int startCapFillIndex = capIndex + 2 * (extra - extraOffset) + 4;
+            int capFillIndex = startCapFillIndex;
+            for (int i = 0; i < extra + 2; i += 2) {
+                copyAlphaVertex(&buffer[capFillIndex++], &buffer[1 + i]);
+                // TODO: to support odd numbers of divisions, break here on the last iteration
+                copyAlphaVertex(&buffer[capFillIndex++], &buffer[startCapFillIndex - 3 - i]);
+            }
+        } else {
+            int capFillIndex = 6 * vertices.size() + 2 + 6 * extra - (extra + 2);
+            for (int i = 0; i < extra + 2; i += 2) {
+                copyAlphaVertex(&buffer[capFillIndex++], &buffer[capIndex + 1 + i]);
+                // TODO: to support odd numbers of divisions, break here on the last iteration
+                copyAlphaVertex(&buffer[capFillIndex++], &buffer[capIndex + 3 + 2 * extra - i]);
+            }
+        }
+        return;
+    }
+    if (isFirst) {
+        copyAlphaVertex(&buffer[0], &buffer[postCapIndex + 2]);
+        copyAlphaVertex(&buffer[1], &buffer[postCapIndex + 3]);
+        copyAlphaVertex(&buffer[postCapIndex + 4], &buffer[1]); // degenerate tris (the only two!)
+        copyAlphaVertex(&buffer[postCapIndex + 5], &buffer[postCapIndex + 1]);
+    } else {
+        copyAlphaVertex(&buffer[6 * vertices.size()], &buffer[postCapIndex + 1]);
+        copyAlphaVertex(&buffer[6 * vertices.size() + 1], &buffer[postCapIndex + 3]);
+    }
+}
+
+/*
+the geometry for an aa, capped stroke consists of the following:
+
+       # vertices       |    function
+----------------------------------------------------------------------
+a) 2                    | Start AA perimeter
+b) 2, 2 * roundDivOff   | First half of begin cap's perimeter
+                        |
+   2 * middlePts        | 'Outer' or 'Top' AA perimeter half (between caps)
+                        |
+a) 4                    | End cap's
+b) 2, 2 * roundDivs, 2  |    AA perimeter
+                        |
+   2 * middlePts        | 'Inner' or 'bottom' AA perimeter half
+                        |
+a) 6                    | Begin cap's perimeter
+b) 2, 2*(rD - rDO + 1), | Last half of begin cap's perimeter
+       roundDivs, 2     |
+                        |
+   2 * middlePts        | Stroke's full opacity center strip
+                        |
+a) 2                    | end stroke
+b) 2, roundDivs         |    (and end cap fill, for round)
+
+Notes:
+* rows starting with 'a)' denote the Butt or Square cap vertex use, 'b)' denote Round
+
+* 'middlePts' is (number of points in the unclosed input vertex list, minus 2) times two
+
+* 'roundDivs' or 'rD' is the number of extra vertices (beyond the minimum of 2) that define the
+        round cap's shape, and is at least two. This will increase with cap size to sufficiently
+        define the cap's level of tessellation.
+
+* 'roundDivOffset' or 'rDO' is the point about halfway along the start cap's round perimeter, where
+        the stream of vertices for the AA perimeter starts. By starting and ending the perimeter at
+        this offset, the fill of the stroke is drawn from this point with minimal extra vertices.
+
+This means the outer perimeter starts at:
+    outerIndex = (2) OR (2 + 2 * roundDivOff)
+the inner perimeter (since it is filled in reverse) starts at:
+    innerIndex = outerIndex + (4 * middlePts) + ((4) OR (4 + 2 * roundDivs)) - 1
+the stroke starts at:
+    strokeIndex = innerIndex + 1 + ((6) OR (6 + 3 * roundDivs - 2 * roundDivOffset))
+
+The total needed allocated space is either:
+    2 + 4 + 6 + 2 + 3 * (2 * middlePts) = 14 + 6 * middlePts = 2 + 6 * pts
+or, for rounded caps:
+    (2 + 2 * rDO) + (4 + 2 * rD) + (2 * (rD - rDO + 1)
+            + roundDivs + 4) + (2 + roundDivs) + 3 * (2 * middlePts)
+    = 14 + 6 * middlePts + 6 * roundDivs
+    = 2 + 6 * pts + 6 * roundDivs
+ */
+void getStrokeVerticesFromUnclosedVerticesAA(const PaintInfo& paintInfo,
+        const Vector<Vertex>& vertices, VertexBuffer& vertexBuffer) {
+
+    const int extra = paintInfo.capExtraDivisions();
+    const int allocSize = 6 * vertices.size() + 2 + 6 * extra;
+
+    AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(allocSize);
+
+    const int extraOffset = (extra + 1) / 2;
+    int offset = 2 * (vertices.size() - 2);
+    // there is no outer/inner here, using them for consistency with below approach
+    int currentAAOuterIndex = 2 + 2 * extraOffset;
+    int currentAAInnerIndex = currentAAOuterIndex + (2 * offset) + 3 + (2 * extra);
+    int currentStrokeIndex = currentAAInnerIndex + 7 + (3 * extra - 2 * extraOffset);
+
+    const Vertex* last = &(vertices[0]);
+    const Vertex* current = &(vertices[1]);
+    vec2 lastNormal(current->position[1] - last->position[1],
+            last->position[0] - current->position[0]);
+    lastNormal.normalize();
+
+    // TODO: use normal from bezier traversal for cap, instead of from vertices
+    storeCapAA(paintInfo, vertices, buffer, true, lastNormal, offset);
+
+    for (unsigned int i = 1; i < vertices.size() - 1; i++) {
+        const Vertex* next = &(vertices[i + 1]);
+        vec2 nextNormal(next->position[1] - current->position[1],
+                current->position[0] - next->position[0]);
+        nextNormal.normalize();
+
+        vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
+        vec2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
+
+        vec2 innerOffset = totalOffset;
+        paintInfo.scaleOffsetForStrokeWidth(innerOffset);
+        vec2 outerOffset = innerOffset + AAOffset;
+        innerOffset -= AAOffset;
+
+        AlphaVertex::set(&buffer[currentAAOuterIndex++],
+                current->position[0] + outerOffset.x,
+                current->position[1] + outerOffset.y,
+                0.0f);
+        AlphaVertex::set(&buffer[currentAAOuterIndex++],
+                current->position[0] + innerOffset.x,
+                current->position[1] + innerOffset.y,
+                paintInfo.maxAlpha);
+
+        AlphaVertex::set(&buffer[currentStrokeIndex++],
+                current->position[0] + innerOffset.x,
+                current->position[1] + innerOffset.y,
+                paintInfo.maxAlpha);
+        AlphaVertex::set(&buffer[currentStrokeIndex++],
+                current->position[0] - innerOffset.x,
+                current->position[1] - innerOffset.y,
+                paintInfo.maxAlpha);
+
+        AlphaVertex::set(&buffer[currentAAInnerIndex--],
+                current->position[0] - innerOffset.x,
+                current->position[1] - innerOffset.y,
+                paintInfo.maxAlpha);
+        AlphaVertex::set(&buffer[currentAAInnerIndex--],
+                current->position[0] - outerOffset.x,
+                current->position[1] - outerOffset.y,
+                0.0f);
+
+        current = next;
+        lastNormal = nextNormal;
+    }
+
+    // TODO: use normal from bezier traversal for cap, instead of from vertices
+    storeCapAA(paintInfo, vertices, buffer, false, lastNormal, offset);
+
+    DEBUG_DUMP_ALPHA_BUFFER();
+}
+
+
+void getStrokeVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter,
+        VertexBuffer& vertexBuffer) {
+    AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(6 * perimeter.size() + 8);
+
+    int offset = 2 * perimeter.size() + 3;
+    int currentAAOuterIndex = 0;
+    int currentStrokeIndex = offset;
+    int currentAAInnerIndex = offset * 2;
+
+    const Vertex* last = &(perimeter[perimeter.size() - 1]);
+    const Vertex* current = &(perimeter[0]);
+    vec2 lastNormal(current->position[1] - last->position[1],
+            last->position[0] - current->position[0]);
+    lastNormal.normalize();
+    for (unsigned int i = 0; i < perimeter.size(); i++) {
+        const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
+        vec2 nextNormal(next->position[1] - current->position[1],
+                current->position[0] - next->position[0]);
+        nextNormal.normalize();
+
+        vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
+        vec2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
+
+        vec2 innerOffset = totalOffset;
+        paintInfo.scaleOffsetForStrokeWidth(innerOffset);
+        vec2 outerOffset = innerOffset + AAOffset;
+        innerOffset -= AAOffset;
+
+        AlphaVertex::set(&buffer[currentAAOuterIndex++],
+                current->position[0] + outerOffset.x,
+                current->position[1] + outerOffset.y,
+                0.0f);
+        AlphaVertex::set(&buffer[currentAAOuterIndex++],
+                current->position[0] + innerOffset.x,
+                current->position[1] + innerOffset.y,
+                paintInfo.maxAlpha);
+
+        AlphaVertex::set(&buffer[currentStrokeIndex++],
+                current->position[0] + innerOffset.x,
+                current->position[1] + innerOffset.y,
+                paintInfo.maxAlpha);
+        AlphaVertex::set(&buffer[currentStrokeIndex++],
+                current->position[0] - innerOffset.x,
+                current->position[1] - innerOffset.y,
+                paintInfo.maxAlpha);
+
+        AlphaVertex::set(&buffer[currentAAInnerIndex++],
+                current->position[0] - innerOffset.x,
+                current->position[1] - innerOffset.y,
+                paintInfo.maxAlpha);
+        AlphaVertex::set(&buffer[currentAAInnerIndex++],
+                current->position[0] - outerOffset.x,
+                current->position[1] - outerOffset.y,
+                0.0f);
+
+        last = current;
+        current = next;
+        lastNormal = nextNormal;
+    }
+
+    // wrap each strip around to beginning, creating degenerate tris to bridge strips
+    copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[0]);
+    copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[1]);
+    copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[1]);
+
+    copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset]);
+    copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset + 1]);
+    copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset + 1]);
+
+    copyAlphaVertex(&buffer[currentAAInnerIndex++], &buffer[2 * offset]);
+    copyAlphaVertex(&buffer[currentAAInnerIndex++], &buffer[2 * offset + 1]);
+    // don't need to create last degenerate tri
+
+    DEBUG_DUMP_ALPHA_BUFFER();
+}
+
+void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint,
+        const mat4 *transform, VertexBuffer& vertexBuffer) {
+    ATRACE_CALL();
+
+    const PaintInfo paintInfo(paint, transform);
+
+    Vector<Vertex> tempVertices;
+    float threshInvScaleX = paintInfo.inverseScaleX;
+    float threshInvScaleY = paintInfo.inverseScaleY;
+    if (paintInfo.style == SkPaint::kStroke_Style) {
+        // alter the bezier recursion threshold values we calculate in order to compensate for
+        // expansion done after the path vertices are found
+        SkRect bounds = path.getBounds();
+        if (!bounds.isEmpty()) {
+            threshInvScaleX *= bounds.width() / (bounds.width() + paint->getStrokeWidth());
+            threshInvScaleY *= bounds.height() / (bounds.height() + paint->getStrokeWidth());
+        }
+    }
+
+    // force close if we're filling the path, since fill path expects closed perimeter.
+    bool forceClose = paintInfo.style != SkPaint::kStroke_Style;
+    bool wasClosed = approximatePathOutlineVertices(path, forceClose,
+            threshInvScaleX * threshInvScaleX, threshInvScaleY * threshInvScaleY, tempVertices);
+
+    if (!tempVertices.size()) {
+        // path was empty, return without allocating vertex buffer
+        return;
+    }
+
+#if VERTEX_DEBUG
+    for (unsigned int i = 0; i < tempVertices.size(); i++) {
+        ALOGD("orig path: point at %f %f",
+                tempVertices[i].position[0], tempVertices[i].position[1]);
+    }
+#endif
+
+    if (paintInfo.style == SkPaint::kStroke_Style) {
+        if (!paintInfo.isAA) {
+            if (wasClosed) {
+                getStrokeVerticesFromPerimeter(paintInfo, tempVertices, vertexBuffer);
+            } else {
+                getStrokeVerticesFromUnclosedVertices(paintInfo, tempVertices, vertexBuffer);
+            }
+
+        } else {
+            if (wasClosed) {
+                getStrokeVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer);
+            } else {
+                getStrokeVerticesFromUnclosedVerticesAA(paintInfo, tempVertices, vertexBuffer);
+            }
+        }
+    } else {
+        // For kStrokeAndFill style, the path should be adjusted externally.
+        // It will be treated as a fill here.
+        if (!paintInfo.isAA) {
+            getFillVerticesFromPerimeter(tempVertices, vertexBuffer);
+        } else {
+            getFillVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer);
+        }
+    }
+}
+
+static void expandRectToCoverVertex(SkRect& rect, const Vertex& vertex) {
+    rect.fLeft = fminf(rect.fLeft, vertex.position[0]);
+    rect.fTop = fminf(rect.fTop, vertex.position[1]);
+    rect.fRight = fmaxf(rect.fRight, vertex.position[0]);
+    rect.fBottom = fmaxf(rect.fBottom, vertex.position[1]);
+}
+
+void PathTessellator::tessellateLines(const float* points, int count, SkPaint* paint,
+        const mat4* transform, SkRect& bounds, VertexBuffer& vertexBuffer) {
+    ATRACE_CALL();
+    const PaintInfo paintInfo(paint, transform);
+
+    const int extra = paintInfo.capExtraDivisions();
+    int numLines = count / 4;
+    int lineAllocSize;
+    // pre-allocate space for lines in the buffer, and degenerate tris in between
+    if (paintInfo.isAA) {
+        lineAllocSize = 6 * (2) + 2 + 6 * extra;
+        vertexBuffer.alloc<AlphaVertex>(numLines * lineAllocSize + (numLines - 1) * 2);
+    } else {
+        lineAllocSize = 2 * ((2) + extra);
+        vertexBuffer.alloc<Vertex>(numLines * lineAllocSize + (numLines - 1) * 2);
+    }
+
+    Vector<Vertex> tempVertices;
+    tempVertices.push();
+    tempVertices.push();
+    Vertex* tempVerticesData = tempVertices.editArray();
+    bounds.set(points[0], points[1], points[0], points[1]);
+    for (int i = 0; i < count; i += 4) {
+        Vertex::set(&(tempVerticesData[0]), points[i + 0], points[i + 1]);
+        Vertex::set(&(tempVerticesData[1]), points[i + 2], points[i + 3]);
+
+        if (paintInfo.isAA) {
+            getStrokeVerticesFromUnclosedVerticesAA(paintInfo, tempVertices, vertexBuffer);
+        } else {
+            getStrokeVerticesFromUnclosedVertices(paintInfo, tempVertices, vertexBuffer);
+        }
+
+        // calculate bounds
+        expandRectToCoverVertex(bounds, tempVerticesData[0]);
+        expandRectToCoverVertex(bounds, tempVerticesData[1]);
+    }
+
+    expandBoundsForStroke(bounds, paint, true); // force-expand bounds to incorporate stroke
+
+    // since multiple objects tessellated into buffer, separate them with degen tris
+    if (paintInfo.isAA) {
+        vertexBuffer.createDegenerateSeparators<AlphaVertex>(lineAllocSize);
+    } else {
+        vertexBuffer.createDegenerateSeparators<Vertex>(lineAllocSize);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Simple path line approximation
+///////////////////////////////////////////////////////////////////////////////
+
+void pushToVector(Vector<Vertex>& vertices, float x, float y) {
+    // TODO: make this not yuck
+    vertices.push();
+    Vertex* newVertex = &(vertices.editArray()[vertices.size() - 1]);
+    Vertex::set(newVertex, x, y);
+}
+
+bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool forceClose,
+        float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) {
+    ATRACE_CALL();
+
+    // TODO: to support joins other than sharp miter, join vertices should be labelled in the
+    // perimeter, or resolved into more vertices. Reconsider forceClose-ing in that case.
+    SkPath::Iter iter(path, forceClose);
+    SkPoint pts[4];
+    SkPath::Verb v;
+    while (SkPath::kDone_Verb != (v = iter.next(pts))) {
+            switch (v) {
+            case SkPath::kMove_Verb:
+                pushToVector(outputVertices, pts[0].x(), pts[0].y());
+                ALOGV("Move to pos %f %f", pts[0].x(), pts[0].y());
+                break;
+            case SkPath::kClose_Verb:
+                ALOGV("Close at pos %f %f", pts[0].x(), pts[0].y());
+                break;
+            case SkPath::kLine_Verb:
+                ALOGV("kLine_Verb %f %f -> %f %f", pts[0].x(), pts[0].y(), pts[1].x(), pts[1].y());
+                pushToVector(outputVertices, pts[1].x(), pts[1].y());
+                break;
+            case SkPath::kQuad_Verb:
+                ALOGV("kQuad_Verb");
+                recursiveQuadraticBezierVertices(
+                        pts[0].x(), pts[0].y(),
+                        pts[2].x(), pts[2].y(),
+                        pts[1].x(), pts[1].y(),
+                        sqrInvScaleX, sqrInvScaleY, outputVertices);
+                break;
+            case SkPath::kCubic_Verb:
+                ALOGV("kCubic_Verb");
+                recursiveCubicBezierVertices(
+                        pts[0].x(), pts[0].y(),
+                        pts[1].x(), pts[1].y(),
+                        pts[3].x(), pts[3].y(),
+                        pts[2].x(), pts[2].y(),
+                        sqrInvScaleX, sqrInvScaleY, outputVertices);
+                break;
+            default:
+                break;
+            }
+    }
+
+    int size = outputVertices.size();
+    if (size >= 2 && outputVertices[0].position[0] == outputVertices[size - 1].position[0] &&
+            outputVertices[0].position[1] == outputVertices[size - 1].position[1]) {
+        outputVertices.pop();
+        return true;
+    }
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Bezier approximation
+///////////////////////////////////////////////////////////////////////////////
+
+void PathTessellator::recursiveCubicBezierVertices(
+        float p1x, float p1y, float c1x, float c1y,
+        float p2x, float p2y, float c2x, float c2y,
+        float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) {
+    float dx = p2x - p1x;
+    float dy = p2y - p1y;
+    float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx);
+    float d2 = fabs((c2x - p2x) * dy - (c2y - p2y) * dx);
+    float d = d1 + d2;
+
+    // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
+
+    if (d * d < THRESHOLD * THRESHOLD * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
+        // below thresh, draw line by adding endpoint
+        pushToVector(outputVertices, p2x, p2y);
+    } else {
+        float p1c1x = (p1x + c1x) * 0.5f;
+        float p1c1y = (p1y + c1y) * 0.5f;
+        float p2c2x = (p2x + c2x) * 0.5f;
+        float p2c2y = (p2y + c2y) * 0.5f;
+
+        float c1c2x = (c1x + c2x) * 0.5f;
+        float c1c2y = (c1y + c2y) * 0.5f;
+
+        float p1c1c2x = (p1c1x + c1c2x) * 0.5f;
+        float p1c1c2y = (p1c1y + c1c2y) * 0.5f;
+
+        float p2c1c2x = (p2c2x + c1c2x) * 0.5f;
+        float p2c1c2y = (p2c2y + c1c2y) * 0.5f;
+
+        float mx = (p1c1c2x + p2c1c2x) * 0.5f;
+        float my = (p1c1c2y + p2c1c2y) * 0.5f;
+
+        recursiveCubicBezierVertices(
+                p1x, p1y, p1c1x, p1c1y,
+                mx, my, p1c1c2x, p1c1c2y,
+                sqrInvScaleX, sqrInvScaleY, outputVertices);
+        recursiveCubicBezierVertices(
+                mx, my, p2c1c2x, p2c1c2y,
+                p2x, p2y, p2c2x, p2c2y,
+                sqrInvScaleX, sqrInvScaleY, outputVertices);
+    }
+}
+
+void PathTessellator::recursiveQuadraticBezierVertices(
+        float ax, float ay,
+        float bx, float by,
+        float cx, float cy,
+        float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) {
+    float dx = bx - ax;
+    float dy = by - ay;
+    float d = (cx - bx) * dy - (cy - by) * dx;
+
+    if (d * d < THRESHOLD * THRESHOLD * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
+        // below thresh, draw line by adding endpoint
+        pushToVector(outputVertices, bx, by);
+    } else {
+        float acx = (ax + cx) * 0.5f;
+        float bcx = (bx + cx) * 0.5f;
+        float acy = (ay + cy) * 0.5f;
+        float bcy = (by + cy) * 0.5f;
+
+        // midpoint
+        float mx = (acx + bcx) * 0.5f;
+        float my = (acy + bcy) * 0.5f;
+
+        recursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy,
+                sqrInvScaleX, sqrInvScaleY, outputVertices);
+        recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy,
+                sqrInvScaleX, sqrInvScaleY, outputVertices);
+    }
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/PathTessellator.h b/libs/hwui/PathTessellator.h
new file mode 100644
index 0000000..596d49d
--- /dev/null
+++ b/libs/hwui/PathTessellator.h
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HWUI_PATH_TESSELLATOR_H
+#define ANDROID_HWUI_PATH_TESSELLATOR_H
+
+#include <utils/Vector.h>
+
+#include "Matrix.h"
+#include "Rect.h"
+#include "Vertex.h"
+
+namespace android {
+namespace uirenderer {
+
+class VertexBuffer {
+public:
+    VertexBuffer():
+        mBuffer(0),
+        mSize(0),
+        mCleanupMethod(NULL)
+    {}
+
+    ~VertexBuffer() {
+        if (mCleanupMethod) mCleanupMethod(mBuffer);
+    }
+
+    /**
+       This should be the only method used by the PathTessellator. Subsequent calls to alloc will
+       allocate space within the first allocation (useful if you want to eventually allocate
+       multiple regions within a single VertexBuffer, such as with PathTessellator::tesselateLines()
+     */
+    template <class TYPE>
+    TYPE* alloc(int size) {
+        if (mSize) {
+            TYPE* reallocBuffer = (TYPE*)mReallocBuffer;
+            // already have allocated the buffer, re-allocate space within
+            if (mReallocBuffer != mBuffer) {
+                // not first re-allocation, leave space for degenerate triangles to separate strips
+                reallocBuffer += 2;
+            }
+            mReallocBuffer = reallocBuffer + size;
+            return reallocBuffer;
+        }
+        mSize = size;
+        mReallocBuffer = mBuffer = (void*)new TYPE[size];
+        mCleanupMethod = &(cleanup<TYPE>);
+
+        return (TYPE*)mBuffer;
+    }
+
+    void* getBuffer() const { return mBuffer; }
+    unsigned int getSize() const { return mSize; }
+
+    template <class TYPE>
+    void createDegenerateSeparators(int allocSize) {
+        TYPE* end = (TYPE*)mBuffer + mSize;
+        for (TYPE* degen = (TYPE*)mBuffer + allocSize; degen < end; degen += 2 + allocSize) {
+            memcpy(degen, degen - 1, sizeof(TYPE));
+            memcpy(degen + 1, degen + 2, sizeof(TYPE));
+        }
+    }
+
+private:
+    template <class TYPE>
+    static void cleanup(void* buffer) {
+        delete[] (TYPE*)buffer;
+    }
+
+    void* mBuffer;
+    unsigned int mSize;
+
+    void* mReallocBuffer; // used for multi-allocation
+
+    void (*mCleanupMethod)(void*);
+};
+
+class PathTessellator {
+public:
+    static void expandBoundsForStroke(SkRect& bounds, const SkPaint* paint, bool forceExpand);
+
+    static void tessellatePath(const SkPath& path, const SkPaint* paint,
+            const mat4 *transform, VertexBuffer& vertexBuffer);
+
+    static void tessellateLines(const float* points, int count, SkPaint* paint,
+            const mat4* transform, SkRect& bounds, VertexBuffer& vertexBuffer);
+
+private:
+    static bool approximatePathOutlineVertices(const SkPath &path, bool forceClose,
+        float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex> &outputVertices);
+
+/*
+  endpoints a & b,
+  control c
+ */
+    static void recursiveQuadraticBezierVertices(
+            float ax, float ay,
+            float bx, float by,
+            float cx, float cy,
+            float sqrInvScaleX, float sqrInvScaleY,
+            Vector<Vertex> &outputVertices);
+
+/*
+  endpoints p1, p2
+  control c1, c2
+ */
+    static void recursiveCubicBezierVertices(
+            float p1x, float p1y,
+            float c1x, float c1y,
+            float p2x, float p2y,
+            float c2x, float c2y,
+            float sqrInvScaleX, float sqrInvScaleY,
+            Vector<Vertex> &outputVertices);
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_PATH_TESSELLATOR_H
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index 7e3aacf..b1df980 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -81,8 +81,6 @@
 
 #define PROGRAM_IS_SIMPLE_GRADIENT 41
 
-#define PROGRAM_IS_VERTEX_SHAPE_SHIFT 42
-
 ///////////////////////////////////////////////////////////////////////////////
 // Types
 ///////////////////////////////////////////////////////////////////////////////
@@ -129,8 +127,7 @@
     bool hasBitmap;
     bool isBitmapNpot;
 
-    bool isAA;
-    bool isVertexShape;
+    bool isAA; // drawing with a per-vertex alpha
 
     bool hasGradient;
     Gradient gradientType;
@@ -168,7 +165,6 @@
         hasTextureTransform = false;
 
         isAA = false;
-        isVertexShape = false;
 
         modulate = false;
 
@@ -263,7 +259,6 @@
         if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT;
         if (hasGammaCorrection) key |= programid(0x1) << PROGRAM_HAS_GAMMA_CORRECTION;
         if (isSimpleGradient) key |= programid(0x1) << PROGRAM_IS_SIMPLE_GRADIENT;
-        if (isVertexShape) key |= programid(0x1) << PROGRAM_IS_VERTEX_SHAPE_SHIFT;
         return key;
     }
 
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index f536ade..fb00335 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -40,9 +40,6 @@
         "attribute vec4 position;\n";
 const char* gVS_Header_Attributes_TexCoords =
         "attribute vec2 texCoords;\n";
-const char* gVS_Header_Attributes_AALineParameters =
-        "attribute float vtxWidth;\n"
-        "attribute float vtxLength;\n";
 const char* gVS_Header_Attributes_AAVertexShapeParameters =
         "attribute float vtxAlpha;\n";
 const char* gVS_Header_Uniforms_TextureTransform =
@@ -68,9 +65,6 @@
         "uniform mediump vec2 textureDimension;\n";
 const char* gVS_Header_Varyings_HasTexture =
         "varying vec2 outTexCoords;\n";
-const char* gVS_Header_Varyings_IsAALine =
-        "varying float widthProportion;\n"
-        "varying float lengthProportion;\n";
 const char* gVS_Header_Varyings_IsAAVertexShape =
         "varying float alpha;\n";
 const char* gVS_Header_Varyings_HasBitmap =
@@ -129,9 +123,6 @@
         "    gl_Position = projection * transform * position;\n";
 const char* gVS_Main_PointSize =
         "    gl_PointSize = pointSize;\n";
-const char* gVS_Main_AALine =
-        "    widthProportion = vtxWidth;\n"
-        "    lengthProportion = vtxLength;\n";
 const char* gVS_Main_AAVertexShape =
         "    alpha = vtxAlpha;\n";
 const char* gVS_Footer =
@@ -149,9 +140,6 @@
         "precision mediump float;\n\n";
 const char* gFS_Uniforms_Color =
         "uniform vec4 color;\n";
-const char* gFS_Uniforms_AALine =
-        "uniform float boundaryWidth;\n"
-        "uniform float boundaryLength;\n";
 const char* gFS_Header_Uniforms_PointHasBitmap =
         "uniform vec2 textureDimension;\n"
         "uniform float pointSize;\n";
@@ -259,9 +247,6 @@
         "    fragColor = color;\n";
 const char* gFS_Main_ModulateColor =
         "    fragColor *= color.a;\n";
-const char* gFS_Main_AccountForAALine =
-        "    fragColor *= (1.0 - smoothstep(boundaryWidth, 0.5, abs(0.5 - widthProportion)))\n"
-        "               * (1.0 - smoothstep(boundaryLength, 0.5, abs(0.5 - lengthProportion)));\n";
 const char* gFS_Main_AccountForAAVertexShape =
         "    fragColor *= alpha;\n";
 
@@ -472,11 +457,7 @@
         shader.append(gVS_Header_Attributes_TexCoords);
     }
     if (description.isAA) {
-        if (description.isVertexShape) {
-            shader.append(gVS_Header_Attributes_AAVertexShapeParameters);
-        } else {
-            shader.append(gVS_Header_Attributes_AALineParameters);
-        }
+        shader.append(gVS_Header_Attributes_AAVertexShapeParameters);
     }
     // Uniforms
     shader.append(gVS_Header_Uniforms);
@@ -497,11 +478,7 @@
         shader.append(gVS_Header_Varyings_HasTexture);
     }
     if (description.isAA) {
-        if (description.isVertexShape) {
-            shader.append(gVS_Header_Varyings_IsAAVertexShape);
-        } else {
-            shader.append(gVS_Header_Varyings_IsAALine);
-        }
+        shader.append(gVS_Header_Varyings_IsAAVertexShape);
     }
     if (description.hasGradient) {
         shader.append(gVS_Header_Varyings_HasGradient[gradientIndex(description)]);
@@ -520,11 +497,7 @@
             shader.append(gVS_Main_OutTexCoords);
         }
         if (description.isAA) {
-            if (description.isVertexShape) {
-                shader.append(gVS_Main_AAVertexShape);
-            } else {
-                shader.append(gVS_Main_AALine);
-            }
+            shader.append(gVS_Main_AAVertexShape);
         }
         if (description.hasBitmap) {
             shader.append(description.isPoint ?
@@ -574,11 +547,7 @@
         shader.append(gVS_Header_Varyings_HasTexture);
     }
     if (description.isAA) {
-        if (description.isVertexShape) {
-            shader.append(gVS_Header_Varyings_IsAAVertexShape);
-        } else {
-            shader.append(gVS_Header_Varyings_IsAALine);
-        }
+        shader.append(gVS_Header_Varyings_IsAAVertexShape);
     }
     if (description.hasGradient) {
         shader.append(gVS_Header_Varyings_HasGradient[gradientIndex(description)]);
@@ -603,9 +572,6 @@
     } else if (description.hasExternalTexture) {
         shader.append(gFS_Uniforms_ExternalTextureSampler);
     }
-    if (description.isAA && !description.isVertexShape) {
-        shader.append(gFS_Uniforms_AALine);
-    }
     if (description.hasGradient) {
         shader.append(gFS_Uniforms_GradientSampler[gradientIndex(description)]);
     }
@@ -618,8 +584,7 @@
 
     // Optimization for common cases
     if (!description.isAA && !blendFramebuffer &&
-            description.colorOp == ProgramDescription::kColorNone &&
-            !description.isPoint && !description.isVertexShape) {
+            description.colorOp == ProgramDescription::kColorNone && !description.isPoint) {
         bool fast = false;
 
         const bool noShader = !description.hasGradient && !description.hasBitmap;
@@ -754,11 +719,7 @@
         shader.append(gFS_Main_ApplyColorOp[description.colorOp]);
 
         if (description.isAA) {
-            if (description.isVertexShape) {
-                shader.append(gFS_Main_AccountForAAVertexShape);
-            } else {
-                shader.append(gFS_Main_AccountForAALine);
-            }
+            shader.append(gFS_Main_AccountForAAVertexShape);
         }
 
         // Output the fragment
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index 22c7dde..87ed825 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -184,6 +184,7 @@
 
 void Snapshot::resetClip(float left, float top, float right, float bottom) {
     clipRect = &mClipRectRoot;
+    clipRegion = &mClipRegionRoot;
     setClip(left, top, right, bottom);
 }
 
diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h
index 38455dc..c120428 100644
--- a/libs/hwui/Vertex.h
+++ b/libs/hwui/Vertex.h
@@ -68,25 +68,6 @@
     }
 }; // struct AlphaVertex
 
-/**
- * Simple structure to describe a vertex with a position and an alpha value.
- */
-struct AAVertex : Vertex {
-    float width;
-    float length;
-
-    static inline void set(AAVertex* vertex, float x, float y, float width, float length) {
-        Vertex::set(vertex, x, y);
-        vertex[0].width = width;
-        vertex[0].length = length;
-    }
-
-    static inline void setColor(AAVertex* vertex, float width, float length) {
-        vertex[0].width = width;
-        vertex[0].length = length;
-    }
-}; // struct AlphaVertex
-
 }; // namespace uirenderer
 }; // namespace android
 
diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp
index f653592..24b0523 100644
--- a/libs/hwui/font/CacheTexture.cpp
+++ b/libs/hwui/font/CacheTexture.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <SkGlyph.h>
 #include <utils/Log.h>
 
 #include "Debug.h"
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
index 128e8b8..1afcb46 100644
--- a/libs/hwui/font/Font.cpp
+++ b/libs/hwui/font/Font.cpp
@@ -20,6 +20,7 @@
 
 #include <utils/JenkinsHash.h>
 
+#include <SkGlyph.h>
 #include <SkUtils.h>
 
 #include "Debug.h"
diff --git a/media/tests/omxjpegdecoder/SkOmxPixelRef.h b/media/tests/omxjpegdecoder/SkOmxPixelRef.h
index afedcbd..374604c 100644
--- a/media/tests/omxjpegdecoder/SkOmxPixelRef.h
+++ b/media/tests/omxjpegdecoder/SkOmxPixelRef.h
@@ -33,6 +33,7 @@
      //! Return the allocation size for the pixels
     size_t getSize() const { return mSize; }
 
+    SK_DECLARE_UNFLATTENABLE_OBJECT()
 protected:
     // overrides from SkPixelRef
     virtual void* onLockPixels(SkColorTable**);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 16007c4..5838526 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -112,7 +112,6 @@
         super(context, dbNameForUser(userHandle), null, DATABASE_VERSION);
         mContext = context;
         mUserHandle = userHandle;
-        setWriteAheadLoggingEnabled(true);
     }
 
     public static boolean isValidTable(String name) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 485908e..45319a8 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -775,7 +775,7 @@
      * @returns whether the database needs to be updated or not, also modifying
      *     'initialValues' if needed.
      */
-    private boolean parseProviderList(Uri url, ContentValues initialValues) {
+    private boolean parseProviderList(Uri url, ContentValues initialValues, int desiredUser) {
         String value = initialValues.getAsString(Settings.Secure.VALUE);
         String newProviders = null;
         if (value != null && value.length() > 1) {
@@ -788,7 +788,7 @@
                 String providers = "";
                 String[] columns = {Settings.Secure.VALUE};
                 String where = Settings.Secure.NAME + "=\'" + Settings.Secure.LOCATION_PROVIDERS_ALLOWED + "\'";
-                Cursor cursor = query(url, columns, where, null, null);
+                Cursor cursor = queryForUser(url, columns, where, null, null, desiredUser);
                 if (cursor != null && cursor.getCount() == 1) {
                     try {
                         cursor.moveToFirst();
@@ -865,7 +865,7 @@
         // Support enabling/disabling a single provider (using "+" or "-" prefix)
         String name = initialValues.getAsString(Settings.Secure.NAME);
         if (Settings.Secure.LOCATION_PROVIDERS_ALLOWED.equals(name)) {
-            if (!parseProviderList(url, initialValues)) return null;
+            if (!parseProviderList(url, initialValues, desiredUserHandle)) return null;
         }
 
         // If this is an insert() of a key that has been migrated to the global store,
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 48edc73..c40e26d 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -189,7 +189,7 @@
     <string name="quick_settings_location_label" msgid="3292451598267467545">"الموقع المستخدم"</string>
     <string name="quick_settings_media_device_label" msgid="1302906836372603762">"جهاز الوسائط"</string>
     <string name="quick_settings_rssi_label" msgid="7725671335550695589">"RSSI"</string>
-    <string name="quick_settings_rssi_emergency_only" msgid="2713774041672886750">"مكالمات طوارئ فقط"</string>
+    <string name="quick_settings_rssi_emergency_only" msgid="2713774041672886750">"مكالمات الطوارئ فقط"</string>
     <string name="quick_settings_settings_label" msgid="5326556592578065401">"الإعدادات"</string>
     <string name="quick_settings_time_label" msgid="4635969182239736408">"الوقت"</string>
     <string name="quick_settings_user_label" msgid="5238995632130897840">"أنا"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 580bfe7..8c2dd8e 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -147,7 +147,7 @@
     <string name="accessibility_ringer_silent" msgid="9061243307939135383">"Mode silenci."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"S\'ha omès <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificació omesa."</string>
-    <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Àrea de notificacions"</string>
+    <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Capa de notificació."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Configuració ràpida."</string>
     <string name="accessibility_desc_recent_apps" msgid="9014032916410590027">"Aplicacions recents."</string>
     <string name="accessibility_quick_settings_user" msgid="1104846699869476855">"Usuari <xliff:g id="USER">%s</xliff:g>."</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 0789b29..d619de1 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -133,7 +133,7 @@
     <string name="accessibility_data_connection_wifi" msgid="2324496756590645221">"Wi-Fi"</string>
     <string name="accessibility_no_sim" msgid="8274017118472455155">"No hay tarjeta SIM."</string>
     <string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"Conexión mediante Bluetooth"</string>
-    <string name="accessibility_airplane_mode" msgid="834748999790763092">"Modo avión"</string>
+    <string name="accessibility_airplane_mode" msgid="834748999790763092">"Modo de avión"</string>
     <!-- String.format failed for translation -->
     <!-- no translation found for accessibility_battery_level (7451474187113371965) -->
     <skip />
@@ -154,7 +154,7 @@
     <string name="accessibility_quick_settings_wifi" msgid="6099781031669728709">"<xliff:g id="SIGNAL">%1$s</xliff:g>. <xliff:g id="NETWORK">%2$s</xliff:g>"</string>
     <string name="accessibility_quick_settings_mobile" msgid="4876806564086241341">"Móvil <xliff:g id="SIGNAL">%1$s</xliff:g>. <xliff:g id="TYPE">%2$s</xliff:g>. <xliff:g id="NETWORK">%3$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_battery" msgid="1480931583381408972">"Batería <xliff:g id="STATE">%s</xliff:g>"</string>
-    <string name="accessibility_quick_settings_airplane" msgid="4196876722090224753">"Modo avión <xliff:g id="STATE">%s</xliff:g>"</string>
+    <string name="accessibility_quick_settings_airplane" msgid="4196876722090224753">"Modo de avión <xliff:g id="STATE">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarma: <xliff:g id="TIME">%s</xliff:g>"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Datos de 2G-3G inhabilitados"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index fff1aa0..c454bb1 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -157,14 +157,14 @@
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarm ustawiony na <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Wyłączono transmisję danych 2G/3G"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Wyłączono transmisję danych 4G"</string>
-    <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Transmisja danych została wyłączona"</string>
+    <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Wyłączono komórkową transmisję danych"</string>
     <string name="data_usage_disabled_dialog_title" msgid="2086815304858964954">"Wyłączono transmisję danych"</string>
-    <string name="data_usage_disabled_dialog" msgid="3853117269051806280">"Ustawiony limit transmisji danych został osiągnięty."\n\n"Jeśli ponownie włączysz przesyłanie danych, operator może naliczyć dodatkowe opłaty."</string>
+    <string name="data_usage_disabled_dialog" msgid="3853117269051806280">"Osiągnięto określony limit wykorzystania transmisji danych."\n\n"Jeśli ponownie włączysz przesyłanie danych, operator może naliczyć opłaty."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="7729772039208664606">"Włącz transmisję danych"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Brak internetu"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi: połączono"</string>
     <string name="gps_notification_searching_text" msgid="8574247005642736060">"Wyszukiwanie sygnału GPS"</string>
-    <string name="gps_notification_found_text" msgid="4619274244146446464">"Lokalizacja z GPSa"</string>
+    <string name="gps_notification_found_text" msgid="4619274244146446464">"Lokalizacja ustawiona według GPS"</string>
     <string name="accessibility_clear_all" msgid="5235938559247164925">"Usuń wszystkie powiadomienia."</string>
     <string name="status_bar_notification_inspect_item_title" msgid="1163547729015390250">"O aplikacji"</string>
     <string name="close_universe" msgid="3736513750241754348">"Zamknij"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 3d0ddd4..ccf4fd48 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -77,7 +77,7 @@
     <string name="use_ptp_button_title" msgid="7517127540301625751">"Установить как камеру (PTP)"</string>
     <string name="installer_cd_button_title" msgid="2312667578562201583">"Установить приложение"</string>
     <string name="accessibility_back" msgid="567011538994429120">"Назад"</string>
-    <string name="accessibility_home" msgid="8217216074895377641">"Домой"</string>
+    <string name="accessibility_home" msgid="8217216074895377641">"Главная страница"</string>
     <string name="accessibility_menu" msgid="316839303324695949">"Меню"</string>
     <string name="accessibility_recent" msgid="8571350598987952883">"Недавние приложения"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Кнопка переключения способа ввода."</string>
@@ -118,8 +118,8 @@
     <string name="accessibility_two_bars" msgid="6437363648385206679">"два деления"</string>
     <string name="accessibility_three_bars" msgid="2648241415119396648">"три деления"</string>
     <string name="accessibility_signal_full" msgid="9122922886519676839">"надежный сигнал"</string>
-    <string name="accessibility_desc_on" msgid="2385254693624345265">"Вкл."</string>
-    <string name="accessibility_desc_off" msgid="6475508157786853157">"Выкл."</string>
+    <string name="accessibility_desc_on" msgid="2385254693624345265">"ВКЛ"</string>
+    <string name="accessibility_desc_off" msgid="6475508157786853157">"ВЫКЛ"</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Подключено"</string>
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 7666f7d..91214d1 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -197,8 +197,8 @@
     <string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Ej ansluten"</string>
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Inget nätverk"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi är inaktiverat"</string>
-    <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Trådlös skärm"</string>
-    <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Trådlös skärm"</string>
+    <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Wi-Fi visas"</string>
+    <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Trådlös visning"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Ljusstyrka"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
     <string name="status_bar_help_title" msgid="1199237744086469217">"Meddelanden visas här"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index f43eabe..7750e77 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -35,10 +35,10 @@
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Поточні"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Сповіщення"</string>
     <string name="battery_low_title" msgid="2783104807551211639">"Підключіть зарядний пристрій"</string>
-    <string name="battery_low_subtitle" msgid="1752040062087829196">"Акумулятор розряджається."</string>
+    <string name="battery_low_subtitle" msgid="1752040062087829196">"Батарея виснажується."</string>
     <string name="battery_low_percent_format" msgid="1077244949318261761">"Залишилося <xliff:g id="NUMBER">%d%%</xliff:g>"</string>
     <string name="invalid_charger" msgid="4549105996740522523">"Заряджання USB не підтримується."\n"Використовуйте лише наданий у комплекті зарядний пристрій."</string>
-    <string name="battery_low_why" msgid="7279169609518386372">"Використання акумулятора"</string>
+    <string name="battery_low_why" msgid="7279169609518386372">"Викор. батареї"</string>
     <string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Налаштування"</string>
     <string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string>
     <string name="status_bar_settings_airplane" msgid="4879879698500955300">"Режим польоту"</string>
@@ -85,11 +85,11 @@
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Збільшення екрана."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth під’єднано."</string>
     <string name="accessibility_bluetooth_disconnected" msgid="7416648669976870175">"Bluetooth від’єднано."</string>
-    <string name="accessibility_no_battery" msgid="358343022352820946">"Акумулятор розряджений."</string>
-    <string name="accessibility_battery_one_bar" msgid="7774887721891057523">"Заряд акумулятора: одна смужка."</string>
-    <string name="accessibility_battery_two_bars" msgid="8500650438735009973">"Заряд акумулятора: дві смужки."</string>
-    <string name="accessibility_battery_three_bars" msgid="2302983330865040446">"Заряд акумулятора: три смужки."</string>
-    <string name="accessibility_battery_full" msgid="8909122401720158582">"Акумулятор заряджений."</string>
+    <string name="accessibility_no_battery" msgid="358343022352820946">"Немає заряду батареї."</string>
+    <string name="accessibility_battery_one_bar" msgid="7774887721891057523">"Одна смужка заряду батареї."</string>
+    <string name="accessibility_battery_two_bars" msgid="8500650438735009973">"Дві смужки заряду батареї."</string>
+    <string name="accessibility_battery_three_bars" msgid="2302983330865040446">"Три смужки заряду батареї."</string>
+    <string name="accessibility_battery_full" msgid="8909122401720158582">"Повний заряд батареї"</string>
     <string name="accessibility_no_phone" msgid="4894708937052611281">"Немає сигналу телефону."</string>
     <string name="accessibility_phone_one_bar" msgid="687699278132664115">"Одна смужка сигналу телефону."</string>
     <string name="accessibility_phone_two_bars" msgid="8384905382804815201">"Дві смужки сигналу телефону."</string>
@@ -134,7 +134,7 @@
     <string name="accessibility_no_sim" msgid="8274017118472455155">"Немає SIM-карти."</string>
     <string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"Прив’язка Bluetooth."</string>
     <string name="accessibility_airplane_mode" msgid="834748999790763092">"Режим польоту."</string>
-    <string name="accessibility_battery_level" msgid="7451474187113371965">"Заряд акумулятора: <xliff:g id="NUMBER">%d</xliff:g>."</string>
+    <string name="accessibility_battery_level" msgid="7451474187113371965">"Відсотків батареї: <xliff:g id="NUMBER">%d</xliff:g>."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Налаштування системи."</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Сповіщення."</string>
     <string name="accessibility_remove_notification" msgid="3603099514902182350">"Очистити сповіщення."</string>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
index cbe296d..27a3a15 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
@@ -46,7 +46,8 @@
             final String action = intent.getAction();
             if (Intent.ACTION_TIME_TICK.equals(action)
                     || Intent.ACTION_TIME_CHANGED.equals(action)
-                    || Intent.ACTION_TIMEZONE_CHANGED.equals(action)) {
+                    || Intent.ACTION_TIMEZONE_CHANGED.equals(action)
+                    || Intent.ACTION_LOCALE_CHANGED.equals(action)) {
                 updateClock();
             }
         }
@@ -122,6 +123,7 @@
                 filter.addAction(Intent.ACTION_TIME_TICK);
                 filter.addAction(Intent.ACTION_TIME_CHANGED);
                 filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+                filter.addAction(Intent.ACTION_LOCALE_CHANGED);
                 mContext.registerReceiver(mIntentReceiver, filter, null, null);
                 updateClock();
             } else {
diff --git a/services/input/SpriteController.cpp b/services/input/SpriteController.cpp
index 1f3d2cf..8163ea0 100644
--- a/services/input/SpriteController.cpp
+++ b/services/input/SpriteController.cpp
@@ -208,8 +208,7 @@
                         surfaceInfo.w, surfaceInfo.h, bpr);
                 surfaceBitmap.setPixels(surfaceInfo.bits);
 
-                SkCanvas surfaceCanvas;
-                surfaceCanvas.setBitmapDevice(surfaceBitmap);
+                SkCanvas surfaceCanvas(surfaceBitmap);
 
                 SkPaint paint;
                 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 78699fb..c8d6ae8 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -677,20 +677,15 @@
         mProvidersByName.remove(provider.getName());
     }
 
-
     /**
-     * Returns "true" if access to the specified location provider is allowed by the current user's
-     * settings. Access to all location providers is forbidden to non-location-provider processes
-     * belonging to background users.
+     * Returns "true" if access to the specified location provider is allowed by the current
+     * user's settings. Access to all location providers is forbidden to non-location-provider
+     * processes belonging to background users.
      *
      * @param provider the name of the location provider
-     * @param uid the requestor's UID
      * @return
      */
-    private boolean isAllowedBySettingsLocked(String provider, int uid) {
-        if (UserHandle.getUserId(uid) != mCurrentUserId && !isUidALocationProvider(uid)) {
-            return false;
-        }
+    private boolean isAllowedByCurrentUserSettingsLocked(String provider) {
         if (mEnabledProviders.contains(provider)) {
             return true;
         }
@@ -704,6 +699,22 @@
     }
 
     /**
+     * Returns "true" if access to the specified location provider is allowed by the specified
+     * user's settings. Access to all location providers is forbidden to non-location-provider
+     * processes belonging to background users.
+     *
+     * @param provider the name of the location provider
+     * @param uid the requestor's UID
+     * @return
+     */
+    private boolean isAllowedByUserSettingsLocked(String provider, int uid) {
+        if (UserHandle.getUserId(uid) != mCurrentUserId && !isUidALocationProvider(uid)) {
+            return false;
+        }
+        return isAllowedByCurrentUserSettingsLocked(provider);
+    }
+
+    /**
      * Returns the permission string associated with the specified resolution level.
      *
      * @param resolutionLevel the resolution level
@@ -892,7 +903,7 @@
                         continue;
                     }
                     if (allowedResolutionLevel >= getMinimumResolutionLevelForProviderUse(name)) {
-                        if (enabledOnly && !isAllowedBySettingsLocked(name, uid)) {
+                        if (enabledOnly && !isAllowedByUserSettingsLocked(name, uid)) {
                             continue;
                         }
                         if (criteria != null && !LocationProvider.propertiesMeetCriteria(
@@ -968,8 +979,7 @@
             LocationProviderInterface p = mProviders.get(i);
             boolean isEnabled = p.isEnabled();
             String name = p.getName();
-            boolean shouldBeEnabled = isAllowedBySettingsLocked(name,
-                    UserHandle.getUid(mCurrentUserId, 0));
+            boolean shouldBeEnabled = isAllowedByCurrentUserSettingsLocked(name);
             if (isEnabled && !shouldBeEnabled) {
                 updateProviderListenersLocked(name, false, mCurrentUserId);
                 changesMade = true;
@@ -1280,7 +1290,7 @@
             oldRecord.disposeLocked(false);
         }
 
-        boolean isProviderEnabled = isAllowedBySettingsLocked(name, uid);
+        boolean isProviderEnabled = isAllowedByUserSettingsLocked(name, uid);
         if (isProviderEnabled) {
             applyRequirementsLocked(name);
         } else {
@@ -1337,7 +1347,7 @@
         // update provider
         for (String provider : providers) {
             // If provider is already disabled, don't need to do anything
-            if (!isAllowedBySettingsLocked(provider, UserHandle.getUid(mCurrentUserId, 0))) {
+            if (!isAllowedByCurrentUserSettingsLocked(provider)) {
                 continue;
             }
 
@@ -1389,7 +1399,7 @@
                 LocationProviderInterface provider = mProvidersByName.get(name);
                 if (provider == null) return null;
 
-                if (!isAllowedBySettingsLocked(name, uid)) return null;
+                if (!isAllowedByUserSettingsLocked(name, uid)) return null;
 
                 Location location = mLastLocation.get(name);
                 if (location == null) {
@@ -1470,7 +1480,7 @@
         final int uid = Binder.getCallingUid();
         final long ident = Binder.clearCallingIdentity();
         try {
-            if (checkLocationAccess(uid, packageName, allowedResolutionLevel)) {
+            if (!checkLocationAccess(uid, packageName, allowedResolutionLevel)) {
                 return false;
             }
         } finally {
@@ -1563,13 +1573,14 @@
                 provider);
         if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
 
+        int uid = Binder.getCallingUid();
         long identity = Binder.clearCallingIdentity();
         try {
             synchronized (mLock) {
                 LocationProviderInterface p = mProvidersByName.get(provider);
                 if (p == null) return false;
 
-                return isAllowedBySettingsLocked(provider, UserHandle.getUid(mCurrentUserId, 0));
+                return isAllowedByUserSettingsLocked(provider, uid);
             }
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -1857,13 +1868,12 @@
             myLocation.setIsFromMockProvider(true);
         }
 
-        if (!passive) {
-            // notify passive provider of the new location
-            mPassiveProvider.updateLocation(myLocation);
-        }
-
         synchronized (mLock) {
-            if (isAllowedBySettingsLocked(provider, UserHandle.getUid(mCurrentUserId, 0))) {
+            if (isAllowedByCurrentUserSettingsLocked(provider)) {
+                if (!passive) {
+                    // notify passive provider of the new location
+                    mPassiveProvider.updateLocation(myLocation);
+                }
                 handleLocationChangedLocked(myLocation, passive);
             }
         }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 44f2d12..1ede6e9 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -7415,9 +7415,7 @@
     }
 
     public void requestBugReport() {
-        // No permission check because this can't do anything harmful --
-        // it will just eventually cause the user to be presented with
-        // a UI to select where the bug report goes.
+        enforceCallingPermission(android.Manifest.permission.DUMP, "requestBugReport");
         SystemProperties.set("ctl.start", "bugreport");
     }
 
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java
index 6022141..fb818d4 100644
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java
+++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java
@@ -122,6 +122,12 @@
                             paint.setStrokeWidth(5);
                         }
                     });
+                    put("30", new DisplayModifier() {
+                        @Override
+                        public void modifyDrawing(Paint paint, Canvas canvas) {
+                            paint.setStrokeWidth(30);
+                        }
+                    });
                 }
             });
             put("strokeCap", new LinkedHashMap<String, DisplayModifier>() {
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 14d1058..2a9016b 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -168,6 +168,15 @@
         </activity>
 
         <activity
+                android:name="ClipRegion3Activity"
+                android:label="_ClipRegion3">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity
                 android:name="DisplayListLayersActivity"
                 android:label="__DisplayListLayers">
             <intent-filter>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegion3Activity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegion3Activity.java
new file mode 100644
index 0000000..6fd03fb
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegion3Activity.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Path;
+import android.os.Bundle;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class ClipRegion3Activity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        final RegionView group = new RegionView(this);
+
+        final TextView text = new TextView(this);
+        text.setText(buildText());
+        group.addView(text);
+
+        setContentView(group);
+    }
+
+    private static CharSequence buildText() {
+        StringBuffer buffer = new StringBuffer();
+        for (int i = 0; i < 10; i++) {
+            buffer.append(LOREM_IPSUM);
+        }
+        return buffer;
+    }
+
+    public static class RegionView extends FrameLayout {
+        private final Path mClipPath = new Path();
+        private float mClipPosition = 0.5f;
+
+        public RegionView(Context c) {
+            super(c);
+            setAlpha(0.5f);
+        }
+
+        @Override
+        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+            super.onSizeChanged(w, h, oldw, oldh);
+            mClipPath.reset();
+            mClipPath.addCircle(0.0f, 0.0f, getWidth() / 4.0f, Path.Direction.CW);
+        }
+
+        @Override
+        protected void dispatchDraw(Canvas canvas) {
+            canvas.drawARGB(255, 255, 255, 255);
+
+            canvas.save(Canvas.MATRIX_SAVE_FLAG);
+            canvas.translate(mClipPosition * getWidth(), getHeight() / 2.0f);
+            canvas.clipPath(mClipPath);
+            canvas.restore();
+
+            super.dispatchDraw(canvas);
+
+            invalidate();
+        }
+    }
+
+    private static final String LOREM_IPSUM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed sagittis molestie aliquam. Donec metus metus, laoreet nec sagittis vitae, ultricies sit amet eros. Suspendisse sed massa sit amet felis consectetur gravida. In vitae erat mi, in egestas nisl. Phasellus quis ipsum massa, at scelerisque arcu. Nam lectus est, pellentesque eget lacinia non, congue vitae augue. Aliquam erat volutpat. Pellentesque bibendum tincidunt viverra. Aliquam erat volutpat. Maecenas pretium vulputate placerat. Nulla varius elementum rutrum. Aenean mollis blandit imperdiet. Pellentesque interdum fringilla ligula.";
+}
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/TestBase.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/TestBase.java
index b5df38d..faef83aa 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/TestBase.java
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/TestBase.java
@@ -45,7 +45,6 @@
     protected Allocation mInPixelsAllocation;
     protected Allocation mInPixelsAllocation2;
     protected Allocation mOutPixelsAllocation;
-    protected ScriptC_msg mMessageScript;
 
     protected ImageProcessingActivity act;
 
@@ -110,7 +109,6 @@
         act = ipact;
         mRS = RenderScript.create(act);
         mRS.setMessageHandler(new MessageProcessor(act));
-        mMessageScript = new ScriptC_msg(mRS);
 
         mInPixelsAllocation = Allocation.createFromBitmap(mRS, b);
         mInPixelsAllocation2 = Allocation.createFromBitmap(mRS, b2);
@@ -129,7 +127,7 @@
 
     final public void runTestSendMessage() {
         runTest();
-        mMessageScript.invoke_sendMsg();
+        mRS.sendMessage(0, null);
     }
 
     public void finish() {
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/msg.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/msg.rs
deleted file mode 100644
index 1a19ffc..0000000
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/msg.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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.
- */
-
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.image)
-
-void sendMsg() {
-    rsSendToClientBlocking(0);
-}
-
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Convolve3x3.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Convolve3x3.java
index 7635e13..d4852f0 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Convolve3x3.java
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Convolve3x3.java
@@ -22,7 +22,7 @@
 import android.util.Log;
 
 public class Convolve3x3 extends TestBase {
-    private ScriptC_convolve3x3 mScript;
+    private ScriptC_ip2_convolve3x3 mScript;
     private ScriptIntrinsicConvolve3x3 mIntrinsic;
 
     private int mWidth;
@@ -47,7 +47,7 @@
             mIntrinsic.setCoefficients(f);
             mIntrinsic.setInput(mInPixelsAllocation);
         } else {
-            mScript = new ScriptC_convolve3x3(mRS, res, R.raw.convolve3x3);
+            mScript = new ScriptC_ip2_convolve3x3(mRS, res, R.raw.ip2_convolve3x3);
             mScript.set_gCoeffs(f);
             mScript.set_gIn(mInPixelsAllocation);
             mScript.set_gWidth(mWidth);
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/convolve3x3.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ip2_convolve3x3.rs
similarity index 100%
rename from tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/convolve3x3.rs
rename to tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ip2_convolve3x3.rs
diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
index 2414d70..8701cc8 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
@@ -188,11 +188,6 @@
         return delegate.mStyle;
     }
 
-    @LayoutlibDelegate
-    /*package*/ static void setGammaForText(float blackGamma, float whiteGamma) {
-        // This is for device testing only: pass
-    }
-
     // ---- Private delegate/helper methods ----
 
     private Typeface_Delegate(String family, int style) {