Merge "Better API for low disk space warnings."
diff --git a/api/current.txt b/api/current.txt
index 536a2c8..50438e6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -20116,6 +20116,9 @@
     method public static android.renderscript.Sampler CLAMP_LINEAR(android.renderscript.RenderScript);
     method public static android.renderscript.Sampler CLAMP_LINEAR_MIP_LINEAR(android.renderscript.RenderScript);
     method public static android.renderscript.Sampler CLAMP_NEAREST(android.renderscript.RenderScript);
+    method public static android.renderscript.Sampler MIRRORED_REPEAT_LINEAR(android.renderscript.RenderScript);
+    method public static android.renderscript.Sampler MIRRORED_REPEAT_LINEAR_MIP_LINEAR(android.renderscript.RenderScript);
+    method public static android.renderscript.Sampler MIRRORED_REPEAT_NEAREST(android.renderscript.RenderScript);
     method public static android.renderscript.Sampler WRAP_LINEAR(android.renderscript.RenderScript);
     method public static android.renderscript.Sampler WRAP_LINEAR_MIP_LINEAR(android.renderscript.RenderScript);
     method public static android.renderscript.Sampler WRAP_NEAREST(android.renderscript.RenderScript);
@@ -20143,6 +20146,7 @@
     enum_constant public static final android.renderscript.Sampler.Value LINEAR;
     enum_constant public static final android.renderscript.Sampler.Value LINEAR_MIP_LINEAR;
     enum_constant public static final android.renderscript.Sampler.Value LINEAR_MIP_NEAREST;
+    enum_constant public static final android.renderscript.Sampler.Value MIRRORED_REPEAT;
     enum_constant public static final android.renderscript.Sampler.Value NEAREST;
     enum_constant public static final android.renderscript.Sampler.Value WRAP;
   }
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 9ad33a5..b678df7 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -227,6 +227,7 @@
         String libDir = (appInfo.nativeLibraryDir != null)
                 ? new File(appInfo.nativeLibraryDir).getCanonicalPath()
                 : null;
+        String externalFilesDir = getExternalFilesDir(null).getCanonicalPath();
 
         // Filters, the scan queue, and the set of resulting entities
         HashSet<String> filterSet = new HashSet<String>();
@@ -254,6 +255,12 @@
         filterSet.add(databaseDir);
         filterSet.remove(sharedPrefsDir);
         fullBackupFileTree(packageName, FullBackup.SHAREDPREFS_TREE_TOKEN, sharedPrefsDir, filterSet, data);
+
+        // getExternalFilesDir() location associated with this app.  Technically there should
+        // not be any files here if the app does not properly have permission to access
+        // external storage, but edge cases happen. fullBackupFileTree() catches
+        // IOExceptions and similar, and treats them as non-fatal, so we rely on that here.
+        fullBackupFileTree(packageName, FullBackup.MANAGED_EXTERNAL_TREE_TOKEN, externalFilesDir, null, data);
     }
 
     /**
@@ -274,6 +281,7 @@
         String spDir;
         String cacheDir;
         String libDir;
+        String efDir;
         String filePath;
 
         ApplicationInfo appInfo = getApplicationInfo();
@@ -287,6 +295,7 @@
             libDir = (appInfo.nativeLibraryDir == null)
                     ? null
                     : new File(appInfo.nativeLibraryDir).getCanonicalPath();
+            efDir = getExternalFilesDir(null).getCanonicalPath();
 
             // Now figure out which well-defined tree the file is placed in, working from
             // most to least specific.  We also specifically exclude the lib and cache dirs.
@@ -315,6 +324,9 @@
         } else if (filePath.startsWith(mainDir)) {
             domain = FullBackup.ROOT_TREE_TOKEN;
             rootpath = mainDir;
+        } else if (filePath.startsWith(efDir)) {
+            domain = FullBackup.MANAGED_EXTERNAL_TREE_TOKEN;
+            rootpath = efDir;
         } else {
             Log.w(TAG, "File " + filePath + " is in an unsupported location; skipping");
             return;
@@ -438,6 +450,8 @@
             basePath = getSharedPrefsFile("foo").getParentFile().getCanonicalPath();
         } else if (domain.equals(FullBackup.CACHE_TREE_TOKEN)) {
             basePath = getCacheDir().getCanonicalPath();
+        } else if (domain.equals(FullBackup.MANAGED_EXTERNAL_TREE_TOKEN)) {
+            basePath = getExternalFilesDir(null).getCanonicalPath();
         } else {
             // Not a supported location
             Log.i(TAG, "Data restored from non-app domain " + domain + ", ignoring");
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index f859599..2fe08f3 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -46,6 +46,7 @@
     public static final String DATA_TREE_TOKEN = "f";
     public static final String DATABASE_TREE_TOKEN = "db";
     public static final String SHAREDPREFS_TREE_TOKEN = "sp";
+    public static final String MANAGED_EXTERNAL_TREE_TOKEN = "ef";
     public static final String CACHE_TREE_TOKEN = "c";
     public static final String SHARED_STORAGE_TOKEN = "shared";
 
diff --git a/core/java/android/speech/tts/FileSynthesisCallback.java b/core/java/android/speech/tts/FileSynthesisCallback.java
index 3e33e8e..ab8f82f 100644
--- a/core/java/android/speech/tts/FileSynthesisCallback.java
+++ b/core/java/android/speech/tts/FileSynthesisCallback.java
@@ -20,10 +20,12 @@
 import android.util.Log;
 
 import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.RandomAccessFile;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
+import java.nio.channels.FileChannel;
 
 /**
  * Speech synthesis request that writes the audio to a WAV file.
@@ -39,16 +41,19 @@
     private static final short WAV_FORMAT_PCM = 0x0001;
 
     private final Object mStateLock = new Object();
-    private final File mFileName;
+
     private int mSampleRateInHz;
     private int mAudioFormat;
     private int mChannelCount;
-    private RandomAccessFile mFile;
+
+    private FileChannel mFileChannel;
+
+    private boolean mStarted = false;
     private boolean mStopped = false;
     private boolean mDone = false;
 
-    FileSynthesisCallback(File fileName) {
-        mFileName = fileName;
+    FileSynthesisCallback(FileChannel fileChannel) {
+        mFileChannel = fileChannel;
     }
 
     @Override
@@ -63,54 +68,23 @@
      * Must be called while holding the monitor on {@link #mStateLock}.
      */
     private void cleanUp() {
-        closeFileAndWidenPermissions();
-        if (mFile != null) {
-            mFileName.delete();
-        }
+        closeFile();
     }
 
     /**
      * Must be called while holding the monitor on {@link #mStateLock}.
      */
-    private void closeFileAndWidenPermissions() {
+    private void closeFile() {
         try {
-            if (mFile != null) {
-                mFile.close();
-                mFile = null;
+            if (mFileChannel != null) {
+                mFileChannel.close();
+                mFileChannel = null;
             }
         } catch (IOException ex) {
-            Log.e(TAG, "Failed to close " + mFileName + ": " + ex);
-        }
-
-        try {
-            // Make the written file readable and writeable by everyone.
-            // This allows the app that requested synthesis to read the file.
-            //
-            // Note that the directory this file was written must have already
-            // been world writeable in order it to have been
-            // written to in the first place.
-            FileUtils.setPermissions(mFileName.getAbsolutePath(), 0666, -1, -1); //-rw-rw-rw
-        } catch (SecurityException se) {
-            Log.e(TAG, "Security exception setting rw permissions on : " + mFileName);
+            Log.e(TAG, "Failed to close output file descriptor", ex);
         }
     }
 
-    /**
-     * Checks whether a given file exists, and deletes it if it does.
-     */
-    private boolean maybeCleanupExistingFile(File file) {
-        if (file.exists()) {
-            Log.v(TAG, "File " + file + " exists, deleting.");
-            if (!file.delete()) {
-                Log.e(TAG, "Failed to delete " + file);
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-
     @Override
     public int getMaxBufferSize() {
         return MAX_AUDIO_BUFFER_SIZE;
@@ -132,25 +106,20 @@
                 if (DBG) Log.d(TAG, "Request has been aborted.");
                 return TextToSpeech.ERROR;
             }
-            if (mFile != null) {
+            if (mStarted) {
                 cleanUp();
                 throw new IllegalArgumentException("FileSynthesisRequest.start() called twice");
             }
-
-            if (!maybeCleanupExistingFile(mFileName)) {
-                return TextToSpeech.ERROR;
-            }
-
+            mStarted = true;
             mSampleRateInHz = sampleRateInHz;
             mAudioFormat = audioFormat;
             mChannelCount = channelCount;
+
             try {
-                mFile = new RandomAccessFile(mFileName, "rw");
-                // Reserve space for WAV header
-                mFile.write(new byte[WAV_HEADER_LENGTH]);
+                mFileChannel.write(ByteBuffer.allocate(WAV_HEADER_LENGTH));
                 return TextToSpeech.SUCCESS;
             } catch (IOException ex) {
-                Log.e(TAG, "Failed to open " + mFileName + ": " + ex);
+                Log.e(TAG, "Failed to write wav header to output file descriptor" + ex);
                 cleanUp();
                 return TextToSpeech.ERROR;
             }
@@ -168,15 +137,15 @@
                 if (DBG) Log.d(TAG, "Request has been aborted.");
                 return TextToSpeech.ERROR;
             }
-            if (mFile == null) {
+            if (mFileChannel == null) {
                 Log.e(TAG, "File not open");
                 return TextToSpeech.ERROR;
             }
             try {
-                mFile.write(buffer, offset, length);
+                mFileChannel.write(ByteBuffer.wrap(buffer,  offset,  length));
                 return TextToSpeech.SUCCESS;
             } catch (IOException ex) {
-                Log.e(TAG, "Failed to write to " + mFileName + ": " + ex);
+                Log.e(TAG, "Failed to write to output file descriptor", ex);
                 cleanUp();
                 return TextToSpeech.ERROR;
             }
@@ -197,21 +166,21 @@
                 if (DBG) Log.d(TAG, "Request has been aborted.");
                 return TextToSpeech.ERROR;
             }
-            if (mFile == null) {
+            if (mFileChannel == null) {
                 Log.e(TAG, "File not open");
                 return TextToSpeech.ERROR;
             }
             try {
                 // Write WAV header at start of file
-                mFile.seek(0);
-                int dataLength = (int) (mFile.length() - WAV_HEADER_LENGTH);
-                mFile.write(
+                mFileChannel.position(0);
+                int dataLength = (int) (mFileChannel.size() - WAV_HEADER_LENGTH);
+                mFileChannel.write(
                         makeWavHeader(mSampleRateInHz, mAudioFormat, mChannelCount, dataLength));
-                closeFileAndWidenPermissions();
+                closeFile();
                 mDone = true;
                 return TextToSpeech.SUCCESS;
             } catch (IOException ex) {
-                Log.e(TAG, "Failed to write to " + mFileName + ": " + ex);
+                Log.e(TAG, "Failed to write to output file descriptor", ex);
                 cleanUp();
                 return TextToSpeech.ERROR;
             }
@@ -226,7 +195,7 @@
         }
     }
 
-    private byte[] makeWavHeader(int sampleRateInHz, int audioFormat, int channelCount,
+    private ByteBuffer makeWavHeader(int sampleRateInHz, int audioFormat, int channelCount,
             int dataLength) {
         // TODO: is AudioFormat.ENCODING_DEFAULT always the same as ENCODING_PCM_16BIT?
         int sampleSizeInBytes = (audioFormat == AudioFormat.ENCODING_PCM_8BIT ? 1 : 2);
@@ -251,8 +220,9 @@
         header.putShort(bitsPerSample);
         header.put(new byte[]{ 'd', 'a', 't', 'a' });
         header.putInt(dataLength);
+        header.flip();
 
-        return headerBuf;
+        return header;
     }
 
 }
diff --git a/core/java/android/speech/tts/ITextToSpeechService.aidl b/core/java/android/speech/tts/ITextToSpeechService.aidl
index 6982029..b7bc70c 100644
--- a/core/java/android/speech/tts/ITextToSpeechService.aidl
+++ b/core/java/android/speech/tts/ITextToSpeechService.aidl
@@ -18,6 +18,7 @@
 
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
 import android.speech.tts.ITextToSpeechCallback;
 
 /**
@@ -44,11 +45,12 @@
      * @param callingInstance a binder representing the identity of the calling
      *        TextToSpeech object.
      * @param text The text to synthesize.
-     * @param filename The file to write the synthesized audio to.
+     * @param fileDescriptor The file descriptor to write the synthesized audio to. Has to be
+              writable.
      * @param param Request parameters.
      */
-    int synthesizeToFile(in IBinder callingInstance, in String text,
-        in String filename, in Bundle params);
+    int synthesizeToFileDescriptor(in IBinder callingInstance, in String text,
+        in ParcelFileDescriptor fileDescriptor, in Bundle params);
 
     /**
      * Plays an existing audio resource.
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index c1af7a5..73d400e 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -27,11 +27,15 @@
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
 
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -1225,8 +1229,29 @@
         return runAction(new Action<Integer>() {
             @Override
             public Integer run(ITextToSpeechService service) throws RemoteException {
-                return service.synthesizeToFile(getCallerIdentity(), text, filename,
-                        getParams(params));
+                ParcelFileDescriptor fileDescriptor;
+                int returnValue;
+                try {
+                    File file = new File(filename);
+                    if(file.exists() && !file.canWrite()) {
+                        Log.e(TAG, "Can't write to " + filename);
+                        return ERROR;
+                    }
+                    fileDescriptor = ParcelFileDescriptor.open(file,
+                            ParcelFileDescriptor.MODE_WRITE_ONLY |
+                            ParcelFileDescriptor.MODE_CREATE |
+                            ParcelFileDescriptor.MODE_TRUNCATE);
+                    returnValue = service.synthesizeToFileDescriptor(getCallerIdentity(), text,
+                            fileDescriptor, getParams(params));
+                    fileDescriptor.close();
+                    return returnValue;
+                } catch (FileNotFoundException e) {
+                    Log.e(TAG, "Opening file " + filename + " failed", e);
+                    return ERROR;
+                } catch (IOException e) {
+                    Log.e(TAG, "Closing file " + filename + " failed", e);
+                    return ERROR;
+                }
             }
         }, ERROR, "synthesizeToFile");
     }
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index 4054740..1bcf3e0 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -26,6 +26,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.MessageQueue;
+import android.os.ParcelFileDescriptor;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.provider.Settings;
@@ -33,7 +34,8 @@
 import android.text.TextUtils;
 import android.util.Log;
 
-import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
 import java.util.HashMap;
 import java.util.Locale;
 import java.util.Set;
@@ -654,19 +656,19 @@
         }
     }
 
-    private class SynthesisToFileSpeechItem extends SynthesisSpeechItem {
-        private final File mFile;
+    private class SynthesisToFileSpeechDescriptorItem extends SynthesisSpeechItem {
+        private final FileDescriptor mFileDescriptor;
 
-        public SynthesisToFileSpeechItem(Object callerIdentity, int callerUid, int callerPid,
-                Bundle params, String text,
-                File file) {
+        public SynthesisToFileSpeechDescriptorItem(Object callerIdentity, int callerUid,
+                int callerPid, Bundle params, String text, FileDescriptor fileDescriptor) {
             super(callerIdentity, callerUid, callerPid, params, text);
-            mFile = file;
+            mFileDescriptor = fileDescriptor;
         }
 
         @Override
         protected AbstractSynthesisCallback createSynthesisCallback() {
-            return new FileSynthesisCallback(mFile);
+            FileOutputStream fileOutputStream = new FileOutputStream(mFileDescriptor);
+            return new FileSynthesisCallback(fileOutputStream.getChannel());
         }
 
         @Override
@@ -797,15 +799,15 @@
         }
 
         @Override
-        public int synthesizeToFile(IBinder caller, String text, String filename,
-                Bundle params) {
-            if (!checkNonNull(caller, text, filename, params)) {
+        public int synthesizeToFileDescriptor(IBinder caller, String text, ParcelFileDescriptor
+                fileDescriptor, Bundle params) {
+            if (!checkNonNull(caller, text, fileDescriptor, params)) {
                 return TextToSpeech.ERROR;
             }
 
-            File file = new File(filename);
-            SpeechItem item = new SynthesisToFileSpeechItem(caller, Binder.getCallingUid(),
-                    Binder.getCallingPid(), params, text, file);
+            SpeechItem item = new SynthesisToFileSpeechDescriptorItem(caller, Binder.getCallingUid(),
+                    Binder.getCallingPid(), params, text,
+                    fileDescriptor.getFileDescriptor());
             return mSynthHandler.enqueueSpeechItem(TextToSpeech.QUEUE_ADD, item);
         }
 
diff --git a/core/java/android/test/AndroidTestCase.java b/core/java/android/test/AndroidTestCase.java
index 0c8cbe6..0635559 100644
--- a/core/java/android/test/AndroidTestCase.java
+++ b/core/java/android/test/AndroidTestCase.java
@@ -20,9 +20,11 @@
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
+
 import junit.framework.TestCase;
 
 import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
 
 /**
  * Extend this if you need to access Resources or other things that depend on Activity Context.
@@ -152,11 +154,11 @@
      * @throws IllegalAccessException
      */
     protected void scrubClass(final Class<?> testCaseClass)
-    throws IllegalAccessException {
+            throws IllegalAccessException {
         final Field[] fields = getClass().getDeclaredFields();
         for (Field field : fields) {
-            final Class<?> fieldClass = field.getDeclaringClass();
-            if (testCaseClass.isAssignableFrom(fieldClass) && !field.getType().isPrimitive()) {
+            if (!field.getType().isPrimitive() &&
+                    !Modifier.isStatic(field.getModifiers())) {
                 try {
                     field.setAccessible(true);
                     field.set(this, null);
@@ -170,6 +172,4 @@
             }
         }
     }
-
-
 }
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index 9ddb32e..28c1058 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -213,6 +213,7 @@
     private OnDoubleTapListener mDoubleTapListener;
 
     private boolean mStillDown;
+    private boolean mDeferConfirmSingleTap;
     private boolean mInLongPress;
     private boolean mAlwaysInTapRegion;
     private boolean mAlwaysInBiggerTapRegion;
@@ -267,8 +268,12 @@
                 
             case TAP:
                 // If the user's finger is still down, do not count it as a tap
-                if (mDoubleTapListener != null && !mStillDown) {
-                    mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent);
+                if (mDoubleTapListener != null) {
+                    if (!mStillDown) {
+                        mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent);
+                    } else {
+                        mDeferConfirmSingleTap = true;
+                    }
                 }
                 break;
 
@@ -533,6 +538,7 @@
             mAlwaysInBiggerTapRegion = true;
             mStillDown = true;
             mInLongPress = false;
+            mDeferConfirmSingleTap = false;
             
             if (mIsLongpressEnabled) {
                 mHandler.removeMessages(LONG_PRESS);
@@ -586,6 +592,9 @@
                 mInLongPress = false;
             } else if (mAlwaysInTapRegion) {
                 handled = mListener.onSingleTapUp(ev);
+                if (mDeferConfirmSingleTap && mDoubleTapListener != null) {
+                    mDoubleTapListener.onSingleTapConfirmed(ev);
+                }
             } else {
 
                 // A fling must travel the minimum tap distance
@@ -612,6 +621,7 @@
                 mVelocityTracker = null;
             }
             mIsDoubleTapping = false;
+            mDeferConfirmSingleTap = false;
             mHandler.removeMessages(SHOW_PRESS);
             mHandler.removeMessages(LONG_PRESS);
             break;
@@ -637,6 +647,7 @@
         mStillDown = false;
         mAlwaysInTapRegion = false;
         mAlwaysInBiggerTapRegion = false;
+        mDeferConfirmSingleTap = false;
         if (mInLongPress) {
             mInLongPress = false;
         }
@@ -649,6 +660,7 @@
         mIsDoubleTapping = false;
         mAlwaysInTapRegion = false;
         mAlwaysInBiggerTapRegion = false;
+        mDeferConfirmSingleTap = false;
         if (mInLongPress) {
             mInLongPress = false;
         }
@@ -671,6 +683,7 @@
 
     private void dispatchLongPress() {
         mHandler.removeMessages(TAP);
+        mDeferConfirmSingleTap = false;
         mInLongPress = true;
         mListener.onLongPress(mCurrentDownEvent);
     }
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 63f0e1f..de64e14 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -32,6 +32,20 @@
 public class Surface implements Parcelable {
     private static final String TAG = "Surface";
 
+    private static native int nativeCreateFromSurfaceTexture(SurfaceTexture surfaceTexture)
+            throws OutOfResourcesException;
+
+    private native Canvas nativeLockCanvas(int nativeObject, Rect dirty);
+    private native void nativeUnlockCanvasAndPost(int nativeObject, Canvas canvas);
+
+    private static native void nativeRelease(int nativeObject);
+    private static native void nativeDestroy(int nativeObject);
+    private static native boolean nativeIsValid(int nativeObject);
+    private static native boolean nativeIsConsumerRunningBehind(int nativeObject);
+    private static native int nativeCopyFrom(int nativeObject, int surfaceControlNativeObject);
+    private static native int nativeReadFromParcel(int nativeObject, Parcel source);
+    private static native void nativeWriteToParcel(int nativeObject, Parcel dest);
+
     public static final Parcelable.Creator<Surface> CREATOR =
             new Parcelable.Creator<Surface>() {
         public Surface createFromParcel(Parcel source) {
@@ -44,33 +58,11 @@
                 return null;
             }
         }
-
         public Surface[] newArray(int size) {
             return new Surface[size];
         }
     };
 
-    /**
-     * Rotation constant: 0 degree rotation (natural orientation)
-     */
-    public static final int ROTATION_0 = 0;
-
-    /**
-     * Rotation constant: 90 degree rotation.
-     */
-    public static final int ROTATION_90 = 1;
-
-    /**
-     * Rotation constant: 180 degree rotation.
-     */
-    public static final int ROTATION_180 = 2;
-
-    /**
-     * Rotation constant: 270 degree rotation.
-     */
-    public static final int ROTATION_270 = 3;
-
-
     private final CloseGuard mCloseGuard = CloseGuard.get();
     private String mName;
 
@@ -95,6 +87,28 @@
 
 
     /**
+     * Rotation constant: 0 degree rotation (natural orientation)
+     */
+    public static final int ROTATION_0 = 0;
+
+    /**
+     * Rotation constant: 90 degree rotation.
+     */
+    public static final int ROTATION_90 = 1;
+
+    /**
+     * Rotation constant: 180 degree rotation.
+     */
+    public static final int ROTATION_180 = 2;
+
+    /**
+     * Rotation constant: 270 degree rotation.
+     */
+    public static final int ROTATION_270 = 3;
+
+
+
+    /**
      * Create an empty surface, which will later be filled in by readFromParcel().
      * @hide
      */
@@ -436,20 +450,4 @@
         if (mNativeObject == 0) throw new NullPointerException(
                 "mNativeObject is null. Have you called release() already?");
     }
-
-    private native int nativeCreateFromSurfaceTexture(SurfaceTexture surfaceTexture)
-            throws OutOfResourcesException;
-
-    private native void nativeRelease(int nativeObject);
-    private native void nativeDestroy(int nativeObject);
-    private native boolean nativeIsValid(int nativeObject);
-
-    private native boolean nativeIsConsumerRunningBehind(int nativeObject);
-
-    private native Canvas nativeLockCanvas(int nativeObject, Rect dirty);
-    private native void nativeUnlockCanvasAndPost(int nativeObject, Canvas canvas);
-    
-    private native int nativeCopyFrom(int nativeObject, int surfaceControlNativeObject);
-    private native int nativeReadFromParcel(int nativeObject, Parcel source);
-    private native void nativeWriteToParcel(int nativeObject, Parcel dest);
 }
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index dd288b9..9f50065 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -30,6 +30,46 @@
  */
 public class SurfaceControl {
     private static final String TAG = "SurfaceControl";
+
+    private static native int nativeCreate(SurfaceSession session, String name,
+            int w, int h, int format, int flags)
+            throws OutOfResourcesException;
+    private static native void nativeRelease(int nativeObject);
+    private static native void nativeDestroy(int nativeObject);
+
+    private static native Bitmap nativeScreenshot(IBinder displayToken,
+            int width, int height, int minLayer, int maxLayer, boolean allLayers);
+
+    private static native void nativeOpenTransaction();
+    private static native void nativeCloseTransaction();
+    private static native void nativeSetAnimationTransaction();
+
+    private static native void nativeSetLayer(int nativeObject, int zorder);
+    private static native void nativeSetPosition(int nativeObject, float x, float y);
+    private static native void nativeSetSize(int nativeObject, int w, int h);
+    private static native void nativeSetTransparentRegionHint(int nativeObject, Region region);
+    private static native void nativeSetAlpha(int nativeObject, float alpha);
+    private static native void nativeSetMatrix(int nativeObject, float dsdx, float dtdx, float dsdy, float dtdy);
+    private static native void nativeSetFlags(int nativeObject, int flags, int mask);
+    private static native void nativeSetWindowCrop(int nativeObject, int l, int t, int r, int b);
+    private static native void nativeSetLayerStack(int nativeObject, int layerStack);
+
+    private static native IBinder nativeGetBuiltInDisplay(int physicalDisplayId);
+    private static native IBinder nativeCreateDisplay(String name, boolean secure);
+    private static native void nativeSetDisplaySurface(
+            IBinder displayToken, int nativeSurfaceObject);
+    private static native void nativeSetDisplayLayerStack(
+            IBinder displayToken, int layerStack);
+    private static native void nativeSetDisplayProjection(
+            IBinder displayToken, int orientation,
+            int l, int t, int r, int b, 
+            int L, int T, int R, int B);
+    private static native boolean nativeGetDisplayInfo(
+            IBinder displayToken, SurfaceControl.PhysicalDisplayInfo outInfo);
+    private static native void nativeBlankDisplay(IBinder displayToken);
+    private static native void nativeUnblankDisplay(IBinder displayToken);
+
+
     private final CloseGuard mCloseGuard = CloseGuard.get();
     private String mName;
     int mNativeObject; // package visibility only for Surface.java access
@@ -532,44 +572,4 @@
             throw new UnsupportedOperationException("Device is headless");
         }
     }
-    
-    
-
-    private native int nativeCreate(SurfaceSession session, String name,
-            int w, int h, int format, int flags)
-            throws OutOfResourcesException;
-    private native void nativeRelease(int nativeObject);
-    private native void nativeDestroy(int nativeObject);
-    
-    private static native Bitmap nativeScreenshot(IBinder displayToken,
-            int width, int height, int minLayer, int maxLayer, boolean allLayers);
-    
-    private static native void nativeOpenTransaction();
-    private static native void nativeCloseTransaction();
-    private static native void nativeSetAnimationTransaction();
-
-    private native void nativeSetLayer(int nativeObject, int zorder);
-    private native void nativeSetPosition(int nativeObject, float x, float y);
-    private native void nativeSetSize(int nativeObject, int w, int h);
-    private native void nativeSetTransparentRegionHint(int nativeObject, Region region);
-    private native void nativeSetAlpha(int nativeObject, float alpha);
-    private native void nativeSetMatrix(int nativeObject, float dsdx, float dtdx, float dsdy, float dtdy);
-    private native void nativeSetFlags(int nativeObject, int flags, int mask);
-    private native void nativeSetWindowCrop(int nativeObject, int l, int t, int r, int b);
-    private native void nativeSetLayerStack(int nativeObject, int layerStack);
-
-    private static native IBinder nativeGetBuiltInDisplay(int physicalDisplayId);
-    private static native IBinder nativeCreateDisplay(String name, boolean secure);
-    private static native void nativeSetDisplaySurface(
-            IBinder displayToken, int nativeSurfaceObject);
-    private static native void nativeSetDisplayLayerStack(
-            IBinder displayToken, int layerStack);
-    private static native void nativeSetDisplayProjection(
-            IBinder displayToken, int orientation,
-            int l, int t, int r, int b, 
-            int L, int T, int R, int B);
-    private static native boolean nativeGetDisplayInfo(
-            IBinder displayToken, SurfaceControl.PhysicalDisplayInfo outInfo);
-    private static native void nativeBlankDisplay(IBinder displayToken);
-    private static native void nativeUnblankDisplay(IBinder displayToken);
 }
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index a4347a4..3ded1cd 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -1254,6 +1254,40 @@
 
         mAutoFillData = new WebViewCore.AutoFillData();
         mEditTextScroller = new Scroller(context);
+
+        // Calculate channel distance
+        calculateChannelDistance(context);
+    }
+
+    /**
+     * Calculate sChannelDistance based on the screen information.
+     * @param context A Context object used to access application assets.
+     */
+    private void calculateChannelDistance(Context context) {
+        // The channel distance is adjusted for density and screen size
+        final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+        final double screenSize = Math.hypot((double)(metrics.widthPixels/metrics.densityDpi),
+                (double)(metrics.heightPixels/metrics.densityDpi));
+        if (screenSize < 3.0) {
+            sChannelDistance = 16;
+        } else if (screenSize < 5.0) {
+            sChannelDistance = 22;
+        } else if (screenSize < 7.0) {
+            sChannelDistance = 28;
+        } else {
+            sChannelDistance = 34;
+        }
+        sChannelDistance = (int)(sChannelDistance * metrics.density);
+        if (sChannelDistance < 16) sChannelDistance = 16;
+
+        if (DebugFlags.WEB_VIEW) {
+            Log.v(LOGTAG, "sChannelDistance : " + sChannelDistance
+                    + ", density : " + metrics.density
+                    + ", screenSize : " + screenSize
+                    + ", metrics.heightPixels : " + metrics.heightPixels
+                    + ", metrics.widthPixels : " + metrics.widthPixels
+                    + ", metrics.densityDpi : " + metrics.densityDpi);
+        }
     }
 
     // WebViewProvider bindings
@@ -5718,32 +5752,13 @@
         }
         return mWebViewPrivate.super_dispatchKeyEvent(event);
     }
-
-    /*
-     * Here is the snap align logic:
-     * 1. If it starts nearly horizontally or vertically, snap align;
-     * 2. If there is a dramitic direction change, let it go;
-     *
-     * Adjustable parameters. Angle is the radians on a unit circle, limited
-     * to quadrant 1. Values range from 0f (horizontal) to PI/2 (vertical)
-     */
-    private static final float HSLOPE_TO_START_SNAP = .25f;
-    private static final float HSLOPE_TO_BREAK_SNAP = .4f;
-    private static final float VSLOPE_TO_START_SNAP = 1.25f;
-    private static final float VSLOPE_TO_BREAK_SNAP = .95f;
-    /*
-     *  These values are used to influence the average angle when entering
-     *  snap mode. If is is the first movement entering snap, we set the average
-     *  to the appropriate ideal. If the user is entering into snap after the
-     *  first movement, then we average the average angle with these values.
-     */
-    private static final float ANGLE_VERT = 2f;
-    private static final float ANGLE_HORIZ = 0f;
-    /*
-     *  The modified moving average weight.
-     *  Formula: MAV[t]=MAV[t-1] + (P[t]-MAV[t-1])/n
-     */
-    private static final float MMA_WEIGHT_N = 5;
+    
+    private static final int SNAP_BOUND = 16;
+    private static int sChannelDistance = 16;
+    private int mFirstTouchX = -1; // the first touched point
+    private int mFirstTouchY = -1;
+    private int mDistanceX = 0;
+    private int mDistanceY = 0;
 
     private boolean inFullScreenMode() {
         return mFullScreenHolder != null;
@@ -5833,12 +5848,6 @@
         }
     }
 
-    private float calculateDragAngle(int dx, int dy) {
-        dx = Math.abs(dx);
-        dy = Math.abs(dy);
-        return (float) Math.atan2(dy, dx);
-    }
-
     /*
     * Common code for single touch and multi-touch.
     * (x, y) denotes current focus point, which is the touch point for single touch
@@ -5864,6 +5873,12 @@
         switch (action) {
             case MotionEvent.ACTION_DOWN: {
                 mConfirmMove = false;
+
+                // Channel Scrolling
+                mFirstTouchX = x;
+                mFirstTouchY = y;
+                mDistanceX = mDistanceY = 0;
+
                 if (!mEditTextScroller.isFinished()) {
                     mEditTextScroller.abortAnimation();
                 }
@@ -6001,20 +6016,16 @@
                         break;
                     }
 
-                    // Only lock dragging to one axis if we don't have a scale in progress.
-                    // Scaling implies free-roaming movement. Note this is only ever a question
-                    // if mZoomManager.supportsPanDuringZoom() is true.
-                    mAverageAngle = calculateDragAngle(deltaX, deltaY);
-                    if (detector == null || !detector.isInProgress()) {
-                        // if it starts nearly horizontal or vertical, enforce it
-                        if (mAverageAngle < HSLOPE_TO_START_SNAP) {
-                            mSnapScrollMode = SNAP_X;
-                            mSnapPositive = deltaX > 0;
-                            mAverageAngle = ANGLE_HORIZ;
-                        } else if (mAverageAngle > VSLOPE_TO_START_SNAP) {
+                    if ((detector == null || !detector.isInProgress())
+                            && SNAP_NONE == mSnapScrollMode) {
+                        int ax = Math.abs(x - mFirstTouchX);
+                        int ay = Math.abs(y - mFirstTouchY);
+                        if (ax < SNAP_BOUND && ay < SNAP_BOUND) {
+                            break;
+                        } else if (ax < SNAP_BOUND) {
                             mSnapScrollMode = SNAP_Y;
-                            mSnapPositive = deltaY > 0;
-                            mAverageAngle = ANGLE_VERT;
+                        } else if (ay < SNAP_BOUND) {
+                            mSnapScrollMode = SNAP_X;
                         }
                     }
 
@@ -6033,31 +6044,21 @@
                 if (deltaX == 0 && deltaY == 0) {
                     keepScrollBarsVisible = true;
                 } else {
-                    mAverageAngle +=
-                        (calculateDragAngle(deltaX, deltaY) - mAverageAngle)
-                        / MMA_WEIGHT_N;
-                    if (mSnapScrollMode != SNAP_NONE) {
-                        if (mSnapScrollMode == SNAP_Y) {
-                            // radical change means getting out of snap mode
-                            if (mAverageAngle < VSLOPE_TO_BREAK_SNAP) {
-                                mSnapScrollMode = SNAP_NONE;
-                            }
-                        }
+                    if (mSnapScrollMode == SNAP_X || mSnapScrollMode == SNAP_Y) {
+                        mDistanceX += Math.abs(deltaX);
+                        mDistanceY += Math.abs(deltaY);
                         if (mSnapScrollMode == SNAP_X) {
-                            // radical change means getting out of snap mode
-                            if (mAverageAngle > HSLOPE_TO_BREAK_SNAP) {
+                            if (mDistanceY > sChannelDistance) {
                                 mSnapScrollMode = SNAP_NONE;
-                            }
+                            } else if (mDistanceX > sChannelDistance) {
+                                mDistanceX = mDistanceY = 0;
                         }
                     } else {
-                        if (mAverageAngle < HSLOPE_TO_START_SNAP) {
-                            mSnapScrollMode = SNAP_X;
-                            mSnapPositive = deltaX > 0;
-                            mAverageAngle = (mAverageAngle + ANGLE_HORIZ) / 2;
-                        } else if (mAverageAngle > VSLOPE_TO_START_SNAP) {
-                            mSnapScrollMode = SNAP_Y;
-                            mSnapPositive = deltaY > 0;
-                            mAverageAngle = (mAverageAngle + ANGLE_VERT) / 2;
+                            if (mDistanceX > sChannelDistance) {
+                                mSnapScrollMode = SNAP_NONE;
+                            } else if (mDistanceY > sChannelDistance) {
+                                mDistanceX = mDistanceY = 0;
+                            }
                         }
                     }
                     if (mSnapScrollMode != SNAP_NONE) {
@@ -6092,6 +6093,7 @@
                 break;
             }
             case MotionEvent.ACTION_UP: {
+                mFirstTouchX  = mFirstTouchY = -1;
                 if (mIsEditingText && mSelectionStarted) {
                     endScrollEdit();
                     mPrivateHandler.sendEmptyMessageDelayed(SCROLL_HANDLE_INTO_VIEW,
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index cad7ae3..a1ced6e 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -607,6 +607,8 @@
         int typefaceIndex = -1;
         int styleIndex = -1;
         boolean allCaps = false;
+        int shadowcolor = 0;
+        float dx = 0, dy = 0, r = 0;
 
         final Resources.Theme theme = context.getTheme();
 
@@ -667,6 +669,22 @@
                 case com.android.internal.R.styleable.TextAppearance_textAllCaps:
                     allCaps = appearance.getBoolean(attr, false);
                     break;
+
+                case com.android.internal.R.styleable.TextAppearance_shadowColor:
+                    shadowcolor = a.getInt(attr, 0);
+                    break;
+
+                case com.android.internal.R.styleable.TextAppearance_shadowDx:
+                    dx = a.getFloat(attr, 0);
+                    break;
+
+                case com.android.internal.R.styleable.TextAppearance_shadowDy:
+                    dy = a.getFloat(attr, 0);
+                    break;
+
+                case com.android.internal.R.styleable.TextAppearance_shadowRadius:
+                    r = a.getFloat(attr, 0);
+                    break;
                 }
             }
 
@@ -690,8 +708,6 @@
         int maxlength = -1;
         CharSequence text = "";
         CharSequence hint = null;
-        int shadowcolor = 0;
-        float dx = 0, dy = 0, r = 0;
         boolean password = false;
         int inputType = EditorInfo.TYPE_NULL;
 
@@ -2331,6 +2347,19 @@
 
         setTypefaceFromAttrs(familyName, typefaceIndex, styleIndex);
 
+        final int shadowcolor = appearance.getInt(
+                com.android.internal.R.styleable.TextAppearance_shadowColor, 0);
+        if (shadowcolor != 0) {
+            final float dx = appearance.getFloat(
+                    com.android.internal.R.styleable.TextAppearance_shadowDx, 0);
+            final float dy = appearance.getFloat(
+                    com.android.internal.R.styleable.TextAppearance_shadowDy, 0);
+            final float r = appearance.getFloat(
+                    com.android.internal.R.styleable.TextAppearance_shadowRadius, 0);
+
+            setShadowLayer(r, dx, dy, shadowcolor);
+        }
+
         if (appearance.getBoolean(com.android.internal.R.styleable.TextAppearance_textAllCaps,
                 false)) {
             setTransformationMethod(new AllCapsTransformationMethod(getContext()));
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index e830492..07f55e0 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -548,7 +548,7 @@
     }
 
     static jint doTextRunCursor(JNIEnv *env, SkPaint* paint, const jchar *text, jint start,
-            jint count, jint flags, jint offset, jint opt) {
+            jint count, jint offset, jint opt) {
         jfloat scalarArray[count];
 
         TextLayout::getTextRunAdvances(paint, text, start, count, start + count,
@@ -592,19 +592,19 @@
     }
 
     static jint getTextRunCursor___C(JNIEnv* env, jobject clazz, SkPaint* paint, jcharArray text,
-            jint contextStart, jint contextCount, jint flags, jint offset, jint cursorOpt) {
+            jint contextStart, jint contextCount, jint offset, jint cursorOpt) {
         jchar* textArray = env->GetCharArrayElements(text, NULL);
-        jint result = doTextRunCursor(env, paint, textArray, contextStart, contextCount, flags,
+        jint result = doTextRunCursor(env, paint, textArray, contextStart, contextCount,
                 offset, cursorOpt);
         env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
         return result;
     }
 
     static jint getTextRunCursor__String(JNIEnv* env, jobject clazz, SkPaint* paint, jstring text,
-            jint contextStart, jint contextEnd, jint flags, jint offset, jint cursorOpt) {
+            jint contextStart, jint contextEnd, jint offset, jint cursorOpt) {
         const jchar* textArray = env->GetStringChars(text, NULL);
         jint result = doTextRunCursor(env, paint, textArray, contextStart,
-                contextEnd - contextStart, flags, offset, cursorOpt);
+                contextEnd - contextStart, offset, cursorOpt);
         env->ReleaseStringChars(text, textArray);
         return result;
     }
diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp
index f8715fe..296d9b2 100644
--- a/core/jni/android/graphics/SurfaceTexture.cpp
+++ b/core/jni/android/graphics/SurfaceTexture.cpp
@@ -19,7 +19,7 @@
 #include <stdio.h>
 
 #include <gui/GLConsumer.h>
-#include <gui/SurfaceTextureClient.h>
+#include <gui/Surface.h>
 
 #include <android_runtime/AndroidRuntime.h>
 
@@ -86,8 +86,8 @@
         JNIEnv* env, jobject thiz)
 {
     sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
-    sp<SurfaceTextureClient> surfaceTextureClient(surfaceTexture != NULL ?
-            new SurfaceTextureClient(surfaceTexture->getBufferQueue()) : NULL);
+    sp<Surface> surfaceTextureClient(surfaceTexture != NULL ?
+            new Surface(surfaceTexture->getBufferQueue()) : NULL);
     return surfaceTextureClient;
 }
 
diff --git a/core/jni/android/graphics/TextLayoutCache.h b/core/jni/android/graphics/TextLayoutCache.h
index 29805ee..6858c0e 100644
--- a/core/jni/android/graphics/TextLayoutCache.h
+++ b/core/jni/android/graphics/TextLayoutCache.h
@@ -24,7 +24,6 @@
 #include <utils/String16.h>
 #include <utils/LruCache.h>
 #include <utils/KeyedVector.h>
-#include <utils/Compare.h>
 #include <utils/RefBase.h>
 #include <utils/Singleton.h>
 
diff --git a/core/jni/android_media_RemoteDisplay.cpp b/core/jni/android_media_RemoteDisplay.cpp
index a340fa1..80d13be 100644
--- a/core/jni/android_media_RemoteDisplay.cpp
+++ b/core/jni/android_media_RemoteDisplay.cpp
@@ -64,7 +64,7 @@
             uint32_t width, uint32_t height, uint32_t flags) {
         JNIEnv* env = AndroidRuntime::getJNIEnv();
 
-        jobject surfaceObj = android_view_Surface_createFromISurfaceTexture(env, bufferProducer);
+        jobject surfaceObj = android_view_Surface_createFromIGraphicBufferProducer(env, bufferProducer);
         if (surfaceObj == NULL) {
             ALOGE("Could not create Surface from surface texture %p provided by media server.",
                   bufferProducer.get());
diff --git a/core/jni/android_opengl_EGL14.cpp b/core/jni/android_opengl_EGL14.cpp
index 9c7124a..26fc261 100644
--- a/core/jni/android_opengl_EGL14.cpp
+++ b/core/jni/android_opengl_EGL14.cpp
@@ -29,7 +29,7 @@
 
 #include <gui/Surface.h>
 #include <gui/GLConsumer.h>
-#include <gui/SurfaceTextureClient.h>
+#include <gui/Surface.h>
 
 #include <ui/ANativeObjectBase.h>
 
@@ -631,7 +631,7 @@
     if (surfaceTexture == NULL)
         goto not_valid_surface;
 
-    window = new android::SurfaceTextureClient(surfaceTexture->getBufferQueue());
+    window = new android::Surface(surfaceTexture->getBufferQueue());
 
     if (window == NULL)
         goto not_valid_surface;
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 6f71868..ab0d38e 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -28,6 +28,7 @@
 #include <android_runtime/android_graphics_SurfaceTexture.h>
 
 #include <gui/Surface.h>
+#include <gui/SurfaceControl.h>
 #include <gui/GLConsumer.h>
 
 #include <ui/Rect.h>
@@ -85,7 +86,7 @@
             env->GetIntField(surfaceObj, gSurfaceClassInfo.mNativeObject));
 }
 
-jobject android_view_Surface_createFromISurfaceTexture(JNIEnv* env,
+jobject android_view_Surface_createFromIGraphicBufferProducer(JNIEnv* env,
         const sp<IGraphicBufferProducer>& bufferProducer) {
     if (bufferProducer == NULL) {
         return NULL;
@@ -111,7 +112,13 @@
 
 // ----------------------------------------------------------------------------
 
-static jint nativeCreateFromSurfaceTexture(JNIEnv* env, jobject surfaceObj,
+static bool isSurfaceValid(const sp<Surface>& sur) {
+    return sur != 0 && sur->getISurfaceTexture() != 0;
+}
+
+// ----------------------------------------------------------------------------
+
+static jint nativeCreateFromSurfaceTexture(JNIEnv* env, jclass clazz,
         jobject surfaceTextureObj) {
     sp<GLConsumer> st(SurfaceTexture_getSurfaceTexture(env, surfaceTextureObj));
     if (st == NULL) {
@@ -127,28 +134,28 @@
         return 0;
     }
 
-    surface->incStrong(surfaceObj);
+    surface->incStrong(clazz);
     return int(surface.get());
 }
 
-static void nativeRelease(JNIEnv* env, jobject surfaceObj, jint nativeObject) {
+static void nativeRelease(JNIEnv* env, jclass clazz, jint nativeObject) {
     sp<Surface> sur(reinterpret_cast<Surface *>(nativeObject));
-    sur->decStrong(surfaceObj);
+    sur->decStrong(clazz);
 }
 
-static void nativeDestroy(JNIEnv* env, jobject surfaceObj, jint nativeObject) {
+static void nativeDestroy(JNIEnv* env, jclass clazz, jint nativeObject) {
     sp<Surface> sur(reinterpret_cast<Surface *>(nativeObject));
-    sur->decStrong(surfaceObj);
+    sur->decStrong(clazz);
 }
 
-static jboolean nativeIsValid(JNIEnv* env, jobject surfaceObj, jint nativeObject) {
+static jboolean nativeIsValid(JNIEnv* env, jclass clazz, jint nativeObject) {
     sp<Surface> sur(reinterpret_cast<Surface *>(nativeObject));
-    return Surface::isValid(sur) ? JNI_TRUE : JNI_FALSE;
+    return isSurfaceValid(sur) ? JNI_TRUE : JNI_FALSE;
 }
 
-static jboolean nativeIsConsumerRunningBehind(JNIEnv* env, jobject surfaceObj, jint nativeObject) {
+static jboolean nativeIsConsumerRunningBehind(JNIEnv* env, jclass clazz, jint nativeObject) {
     sp<Surface> sur(reinterpret_cast<Surface *>(nativeObject));
-    if (!Surface::isValid(sur)) {
+    if (!isSurfaceValid(sur)) {
         doThrowIAE(env);
         return JNI_FALSE;
     }
@@ -176,7 +183,7 @@
 static jobject nativeLockCanvas(JNIEnv* env, jobject surfaceObj, jint nativeObject, jobject dirtyRectObj) {
     sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
 
-    if (!Surface::isValid(surface)) {
+    if (!isSurfaceValid(surface)) {
         doThrowIAE(env);
         return NULL;
     }
@@ -196,8 +203,10 @@
         dirtyRegion.set(Rect(0x3FFF, 0x3FFF));
     }
 
-    Surface::SurfaceInfo info;
-    status_t err = surface->lock(&info, &dirtyRegion);
+    ANativeWindow_Buffer outBuffer;
+    Rect dirtyBounds(dirtyRegion.getBounds());
+    status_t err = surface->lock(&outBuffer, &dirtyBounds);
+    dirtyRegion.set(dirtyBounds);
     if (err < 0) {
         const char* const exception = (err == NO_MEMORY) ?
                 OutOfResourcesException :
@@ -208,18 +217,18 @@
 
     // Associate a SkCanvas object to this surface
     jobject canvasObj = env->GetObjectField(surfaceObj, gSurfaceClassInfo.mCanvas);
-    env->SetIntField(canvasObj, gCanvasClassInfo.mSurfaceFormat, info.format);
+    env->SetIntField(canvasObj, gCanvasClassInfo.mSurfaceFormat, outBuffer.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);
-    if (info.format == PIXEL_FORMAT_RGBX_8888) {
+    ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
+    bitmap.setConfig(convertPixelFormat(outBuffer.format), outBuffer.width, outBuffer.height, bpr);
+    if (outBuffer.format == PIXEL_FORMAT_RGBX_8888) {
         bitmap.setIsOpaque(true);
     }
-    if (info.w > 0 && info.h > 0) {
-        bitmap.setPixels(info.bits);
+    if (outBuffer.width > 0 && outBuffer.height > 0) {
+        bitmap.setPixels(outBuffer.bits);
     } else {
         // be safe with an empty bitmap.
         bitmap.setPixels(NULL);
@@ -263,7 +272,7 @@
     }
 
     sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
-    if (!Surface::isValid(surface)) {
+    if (!isSurfaceValid(surface)) {
         return;
     }
 
@@ -284,7 +293,7 @@
 
 // ----------------------------------------------------------------------------
 
-static jint nativeCopyFrom(JNIEnv* env, jobject surfaceObj,
+static jint nativeCopyFrom(JNIEnv* env, jclass clazz,
         jint nativeObject, jint surfaceControlNativeObj) {
     /*
      * This is used by the WindowManagerService just after constructing
@@ -295,18 +304,18 @@
     sp<SurfaceControl> ctrl(reinterpret_cast<SurfaceControl *>(surfaceControlNativeObj));
     sp<Surface> other(ctrl->getSurface());
     if (other != NULL) {
-        other->incStrong(surfaceObj);
+        other->incStrong(clazz);
     }
 
     sp<Surface> sur(reinterpret_cast<Surface *>(nativeObject));
     if (sur != NULL) {
-        sur->decStrong(surfaceObj);
+        sur->decStrong(clazz);
     }
 
     return int(other.get());
 }
 
-static jint nativeReadFromParcel(JNIEnv* env, jobject surfaceObj,
+static jint nativeReadFromParcel(JNIEnv* env, jclass clazz,
         jint nativeObject, jobject parcelObj) {
     Parcel* parcel = parcelForJavaObject(env, parcelObj);
     if (parcel == NULL) {
@@ -315,16 +324,16 @@
     }
     sp<Surface> self(reinterpret_cast<Surface *>(nativeObject));
     if (self != NULL) {
-        self->decStrong(surfaceObj);
+        self->decStrong(clazz);
     }
     sp<Surface> sur(Surface::readFromParcel(*parcel));
     if (sur != NULL) {
-        sur->incStrong(surfaceObj);
+        sur->incStrong(clazz);
     }
     return int(sur.get());
 }
 
-static void nativeWriteToParcel(JNIEnv* env, jobject surfaceObj,
+static void nativeWriteToParcel(JNIEnv* env, jclass clazz,
         jint nativeObject, jobject parcelObj) {
     Parcel* parcel = parcelForJavaObject(env, parcelObj);
     if (parcel == NULL) {
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 7398895..e477e54 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -120,7 +120,7 @@
 
 // ----------------------------------------------------------------------------
 
-static jint nativeCreate(JNIEnv* env, jobject surfaceObj, jobject sessionObj,
+static jint nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,
         jstring nameStr, jint w, jint h, jint format, jint flags) {
     ScopedUtfChars name(env, nameStr);
     sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj));
@@ -130,19 +130,19 @@
         jniThrowException(env, OutOfResourcesException, NULL);
         return 0;
     }
-    surface->incStrong(surfaceObj);
+    surface->incStrong(clazz);
     return int(surface.get());
 }
 
-static void nativeRelease(JNIEnv* env, jobject surfaceObj, jint nativeObject) {
+static void nativeRelease(JNIEnv* env, jclass clazz, jint nativeObject) {
     sp<SurfaceControl> ctrl(reinterpret_cast<SurfaceControl *>(nativeObject));
-    ctrl->decStrong(surfaceObj);
+    ctrl->decStrong(clazz);
 }
 
-static void nativeDestroy(JNIEnv* env, jobject surfaceObj, jint nativeObject) {
+static void nativeDestroy(JNIEnv* env, jclass clazz, jint nativeObject) {
     sp<SurfaceControl> ctrl(reinterpret_cast<SurfaceControl *>(nativeObject));
     ctrl->clear();
-    ctrl->decStrong(surfaceObj);
+    ctrl->decStrong(clazz);
 }
 
 static inline SkBitmap::Config convertPixelFormat(PixelFormat format) {
@@ -210,7 +210,7 @@
     SurfaceComposerClient::setAnimationTransaction();
 }
 
-static void nativeSetLayer(JNIEnv* env, jobject surfaceObj, jint nativeObject, jint zorder) {
+static void nativeSetLayer(JNIEnv* env, jclass clazz, jint nativeObject, jint zorder) {
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
     status_t err = ctrl->setLayer(zorder);
     if (err < 0 && err != NO_INIT) {
@@ -218,7 +218,7 @@
     }
 }
 
-static void nativeSetPosition(JNIEnv* env, jobject surfaceObj, jint nativeObject, jfloat x, jfloat y) {
+static void nativeSetPosition(JNIEnv* env, jclass clazz, jint nativeObject, jfloat x, jfloat y) {
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
     status_t err = ctrl->setPosition(x, y);
     if (err < 0 && err != NO_INIT) {
@@ -226,7 +226,7 @@
     }
 }
 
-static void nativeSetSize(JNIEnv* env, jobject surfaceObj, jint nativeObject, jint w, jint h) {
+static void nativeSetSize(JNIEnv* env, jclass clazz, jint nativeObject, jint w, jint h) {
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
     status_t err = ctrl->setSize(w, h);
     if (err < 0 && err != NO_INIT) {
@@ -234,7 +234,7 @@
     }
 }
 
-static void nativeSetFlags(JNIEnv* env, jobject surfaceObj, jint nativeObject, jint flags, jint mask) {
+static void nativeSetFlags(JNIEnv* env, jclass clazz, jint nativeObject, jint flags, jint mask) {
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
     status_t err = ctrl->setFlags(flags, mask);
     if (err < 0 && err != NO_INIT) {
@@ -242,7 +242,7 @@
     }
 }
 
-static void nativeSetTransparentRegionHint(JNIEnv* env, jobject surfaceObj, jint nativeObject, jobject regionObj) {
+static void nativeSetTransparentRegionHint(JNIEnv* env, jclass clazz, jint nativeObject, jobject regionObj) {
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
     SkRegion* region = android_graphics_Region_getSkRegion(env, regionObj);
     if (!region) {
@@ -267,7 +267,7 @@
     }
 }
 
-static void nativeSetAlpha(JNIEnv* env, jobject surfaceObj, jint nativeObject, jfloat alpha) {
+static void nativeSetAlpha(JNIEnv* env, jclass clazz, jint nativeObject, jfloat alpha) {
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
     status_t err = ctrl->setAlpha(alpha);
     if (err < 0 && err != NO_INIT) {
@@ -275,7 +275,7 @@
     }
 }
 
-static void nativeSetMatrix(JNIEnv* env, jobject surfaceObj, jint nativeObject,
+static void nativeSetMatrix(JNIEnv* env, jclass clazz, jint nativeObject,
         jfloat dsdx, jfloat dtdx, jfloat dsdy, jfloat dtdy) {
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
     status_t err = ctrl->setMatrix(dsdx, dtdx, dsdy, dtdy);
@@ -284,7 +284,7 @@
     }
 }
 
-static void nativeSetWindowCrop(JNIEnv* env, jobject surfaceObj, jint nativeObject,
+static void nativeSetWindowCrop(JNIEnv* env, jclass clazz, jint nativeObject,
         jint l, jint t, jint r, jint b) {
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
     Rect crop(l, t, r, b);
@@ -294,7 +294,7 @@
     }
 }
 
-static void nativeSetLayerStack(JNIEnv* env, jobject surfaceObj, jint nativeObject, jint layerStack) {
+static void nativeSetLayerStack(JNIEnv* env, jclass clazz, jint nativeObject, jint layerStack) {
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
     status_t err = ctrl->setLayerStack(layerStack);
     if (err < 0 && err != NO_INIT) {
@@ -320,7 +320,7 @@
     sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
     if (token == NULL) return;
     sp<Surface> sur(reinterpret_cast<Surface *>(nativeSurfaceObject));
-    sp<IGraphicBufferProducer> bufferProducer(sur->getSurfaceTexture());
+    sp<IGraphicBufferProducer> bufferProducer(sur->getIGraphicBufferProducer());
     SurfaceComposerClient::setDisplaySurface(token, bufferProducer);
 }
 
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index 87b312f..e75a2d8 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -23,7 +23,7 @@
 #include <ui/Rect.h>
 
 #include <gui/GLConsumer.h>
-#include <gui/SurfaceTextureClient.h>
+#include <gui/Surface.h>
 
 #include <SkBitmap.h>
 #include <SkCanvas.h>
@@ -102,7 +102,7 @@
         jobject surface) {
 
     sp<GLConsumer> glConsumer(SurfaceTexture_getSurfaceTexture(env, surface));
-    sp<ANativeWindow> window = new SurfaceTextureClient(glConsumer->getBufferQueue());
+    sp<ANativeWindow> window = new Surface(glConsumer->getBufferQueue());
 
     window->incStrong(0);
     SET_INT(textureView, gTextureViewClassInfo.nativeWindow, jint(window.get()));
diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
index 199d5bf..37330ec 100644
--- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
@@ -28,7 +28,7 @@
 
 #include <gui/Surface.h>
 #include <gui/GLConsumer.h>
-#include <gui/SurfaceTextureClient.h>
+#include <gui/Surface.h>
 
 #include <SkBitmap.h>
 #include <SkPixelRef.h>
@@ -355,7 +355,7 @@
     
     sp<GLConsumer> glConsumer(SurfaceTexture_getSurfaceTexture(_env, native_window));
 
-    window = new SurfaceTextureClient(glConsumer->getBufferQueue());
+    window = new Surface(glConsumer->getBufferQueue());
     if (window == NULL)
         goto not_valid_surface;
 
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index ac422ea..4cb7888 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -618,7 +618,7 @@
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"verander verrekening van netwerkgebruik"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Laat die program toe om te verander hoe netwerkgebruik teenoor programme gemeet word. Nie vir gebruik deur normale programme nie."</string>
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"kry toegang tot kennisgewings"</string>
-    <string name="permdesc_accessNotifications" msgid="458457742683431387">"Laat die program toe om kennisgewings te herwin, bestudeer en te verwyder, met inbegrip van daardie kennisgewings wat deur ander programme geplaas is."</string>
+    <string name="permdesc_accessNotifications" msgid="458457742683431387">"Laat die program toe om kennisgewings op te haal, te bestudeer en te verwyder, insluitende die kennisgewings wat deur ander programme geplaas is."</string>
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Stel wagwoordreëls"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Beheer lengte en watter karakters wat in die skermontsluit-wagwoorde gebruik word."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Monitor pogings om skerm te ontsluit"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 40fc411..5e3c56b 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1291,7 +1291,7 @@
     <item quantity="one" msgid="8167147081136579439">"1 αποτέλεσμα"</item>
     <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> από <xliff:g id="TOTAL">%d</xliff:g>"</item>
   </plurals>
-    <string name="action_mode_done" msgid="7217581640461922289">"Ολοκληρώθηκε"</string>
+    <string name="action_mode_done" msgid="7217581640461922289">"Τέλος"</string>
     <string name="progress_unmounting" product="nosdcard" msgid="3923810448507612746">"Αποσύνδεση του χώρου αποθήκευσης USB..."</string>
     <string name="progress_unmounting" product="default" msgid="1327894998409537190">"Αφαίρεση κάρτας SD..."</string>
     <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"Διαγραφή χώρου αποθήκευσης USB..."</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index a472123..d74810a 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1062,7 +1062,7 @@
     <string name="noApplications" msgid="2991814273936504689">"Egy alkalmazás sem tudja végrehajtani ezt a műveletet."</string>
     <string name="aerr_title" msgid="1905800560317137752"></string>
     <string name="aerr_application" msgid="932628488013092776">"A(z) <xliff:g id="APPLICATION">%1$s</xliff:g> alkalmazás sajnos leállt."</string>
-    <string name="aerr_process" msgid="4507058997035697579">"Sajnos a(z) <xliff:g id="PROCESS">%1$s</xliff:g> folyamat leállt."</string>
+    <string name="aerr_process" msgid="4507058997035697579">"Sajnos a <xliff:g id="PROCESS">%1$s</xliff:g> alkalmazás leállt."</string>
     <string name="anr_title" msgid="4351948481459135709"></string>
     <string name="anr_activity_application" msgid="1904477189057199066">"A(z) <xliff:g id="APPLICATION">%2$s</xliff:g> nem válaszol."\n\n"Szeretné bezárni?"</string>
     <string name="anr_activity_process" msgid="5776209883299089767">"A(z) <xliff:g id="ACTIVITY">%1$s</xliff:g> tevékenység nem válaszol."\n\n"Szeretné bezárni?"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 186eb3e..7f7d6d2 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1054,7 +1054,7 @@
     <string name="loading" msgid="7933681260296021180">"A carregar…"</string>
     <string name="capital_on" msgid="1544682755514494298">"Activado"</string>
     <string name="capital_off" msgid="6815870386972805832">"Desactivar"</string>
-    <string name="whichApplication" msgid="4533185947064773386">"Concluir acção utilizando"</string>
+    <string name="whichApplication" msgid="4533185947064773386">"Concluir ação utilizando"</string>
     <string name="alwaysUse" msgid="4583018368000610438">"Utilizar por predefinição para esta acção."</string>
     <string name="clearDefaultHintMsg" msgid="3252584689512077257">"Limpar a predefinição nas Definições do Sistema &gt; Aplicações &gt; Transferidas."</string>
     <string name="chooseActivity" msgid="7486876147751803333">"Escolha uma ação"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index e7df782..af28134 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -618,7 +618,7 @@
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"แก้ไขการบันทึกบัญชีการใช้งานเครือข่าย"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"อนุญาตให้แอปพลิเคชันแก้ไขวิธีการบันทึกบัญชีการใช้งานเครือข่ายของแอปพลิเคชัน ไม่ใช้สำหรับแอปพลิเคชันทั่วไป"</string>
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"เข้าถึงการแจ้งเตือน"</string>
-    <string name="permdesc_accessNotifications" msgid="458457742683431387">"ทำให้แอปสามารถเรียกคืน ตรวจสอบ และล้างการแจ้งเตือนได้ ซึ่งรวมถึงการแจ้งเตือนที่โพสต์โดยแอปอื่นๆ ด้วย"</string>
+    <string name="permdesc_accessNotifications" msgid="458457742683431387">"ทำให้แอปสามารถเรียกดู ตรวจสอบ และล้างการแจ้งเตือนได้ ซึ่งรวมถึงการแจ้งเตือนที่โพสต์โดยแอปอื่นๆ ด้วย"</string>
     <string name="policylab_limitPassword" msgid="4497420728857585791">"ตั้งค่ากฎรหัสผ่าน"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"ควบคุมความยาวและอักขระที่อนุญาตให้ใช้ในรหัสผ่านการปลดล็อกหน้าจอ"</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"ตรวจสอบความพยายามในการปลดล็อกหน้าจอ"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 7f80706..ce5fdc06 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1062,7 +1062,7 @@
     <string name="noApplications" msgid="2991814273936504689">"Жодна програма не може виконати цю дію."</string>
     <string name="aerr_title" msgid="1905800560317137752"></string>
     <string name="aerr_application" msgid="932628488013092776">"На жаль, програма <xliff:g id="APPLICATION">%1$s</xliff:g> припинила роботу."</string>
-    <string name="aerr_process" msgid="4507058997035697579">"На жаль, процес <xliff:g id="PROCESS">%1$s</xliff:g> припинився."</string>
+    <string name="aerr_process" msgid="4507058997035697579">"На жаль, програма <xliff:g id="PROCESS">%1$s</xliff:g> припинила роботу."</string>
     <string name="anr_title" msgid="4351948481459135709"></string>
     <string name="anr_activity_application" msgid="1904477189057199066">"Програма <xliff:g id="APPLICATION">%2$s</xliff:g> не відповідає."\n\n"Закрити її?"</string>
     <string name="anr_activity_process" msgid="5776209883299089767">"Дія <xliff:g id="ACTIVITY">%1$s</xliff:g> не відповідає."\n\n"Закінчити її?"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 0fb8a10..a3e5b2c 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3061,6 +3061,14 @@
         <attr name="textColorLink" />
         <!-- Present the text in ALL CAPS. This may use a small-caps form when available. -->
         <attr name="textAllCaps" format="boolean" />
+        <!-- Place a shadow of the specified color behind the text. -->
+        <attr name="shadowColor" format="color" />
+        <!-- Horizontal offset of the shadow. -->
+        <attr name="shadowDx" format="float" />
+        <!-- Vertical offset of the shadow. -->
+        <attr name="shadowDy" format="float" />
+        <!-- Radius of the shadow. -->
+        <attr name="shadowRadius" format="float" />
     </declare-styleable>
     <declare-styleable name="TextClock">
         <!-- Specifies the formatting pattern used to show the time and/or date
@@ -3195,13 +3203,13 @@
              specified number. -->
         <attr name="maxLength" format="integer" min="0" />
         <!-- Place a shadow of the specified color behind the text. -->
-        <attr name="shadowColor" format="color" />
+        <attr name="shadowColor" />
         <!-- Horizontal offset of the shadow. -->
-        <attr name="shadowDx" format="float" />
+        <attr name="shadowDx" />
         <!-- Vertical offset of the shadow. -->
-        <attr name="shadowDy" format="float" />
+        <attr name="shadowDy" />
         <!-- Radius of the shadow. -->
-        <attr name="shadowRadius" format="float" />
+        <attr name="shadowRadius" />
         <attr name="autoLink" />
         <!-- If set to false, keeps the movement method from being set
              to the link movement method even if autoLink causes links
diff --git a/data/keyboards/Generic.kcm b/data/keyboards/Generic.kcm
index 782a701..01d22ee 100644
--- a/data/keyboards/Generic.kcm
+++ b/data/keyboards/Generic.kcm
@@ -480,7 +480,7 @@
 ### Gamepad buttons ###
 
 key BUTTON_A {
-    base:                               fallback BACK
+    base:                               fallback DPAD_CENTER
 }
 
 key BUTTON_B {
@@ -488,7 +488,7 @@
 }
 
 key BUTTON_C {
-    base:                               fallback BACK
+    base:                               fallback DPAD_CENTER
 }
 
 key BUTTON_X {
@@ -496,7 +496,7 @@
 }
 
 key BUTTON_Y {
-    base:                               fallback DPAD_CENTER
+    base:                               fallback BACK
 }
 
 key BUTTON_Z {
@@ -528,7 +528,7 @@
 }
 
 key BUTTON_START {
-    base:                               fallback HOME
+    base:                               fallback DPAD_CENTER
 }
 
 key BUTTON_SELECT {
diff --git a/data/keyboards/Virtual.kcm b/data/keyboards/Virtual.kcm
index d90b790..c4647e0 100644
--- a/data/keyboards/Virtual.kcm
+++ b/data/keyboards/Virtual.kcm
@@ -477,7 +477,7 @@
 ### Gamepad buttons ###
 
 key BUTTON_A {
-    base:                               fallback BACK
+    base:                               fallback DPAD_CENTER
 }
 
 key BUTTON_B {
@@ -485,7 +485,7 @@
 }
 
 key BUTTON_C {
-    base:                               fallback BACK
+    base:                               fallback DPAD_CENTER
 }
 
 key BUTTON_X {
@@ -493,7 +493,7 @@
 }
 
 key BUTTON_Y {
-    base:                               fallback DPAD_CENTER
+    base:                               fallback BACK
 }
 
 key BUTTON_Z {
@@ -525,7 +525,7 @@
 }
 
 key BUTTON_START {
-    base:                               fallback HOME
+    base:                               fallback DPAD_CENTER
 }
 
 key BUTTON_SELECT {
diff --git a/docs/html/about/versions/jelly-bean.jd b/docs/html/about/versions/jelly-bean.jd
index 6cd6f9d..acb2538 100644
--- a/docs/html/about/versions/jelly-bean.jd
+++ b/docs/html/about/versions/jelly-bean.jd
@@ -331,14 +331,14 @@
 appropriate.</p>
 
 <p>For precise control over your app UI, Android 4.2 includes new APIs that let
-you manage layout direction, text direction, text alignment, gravity, and locale
-direction in View components. You can even create custom versions of layout,
-drawables, and other resources for display when a right-to-left script is in
-use.</p>
+you manage layout direction, text direction, text alignment, gravity, and 
+locale direction in View components. You can even create custom versions of
+layout, drawables, and other resources for display when a right-to-left script
+is in use.</p>
 
 <p>To help you debug and optimize your custom right-to-left layouts, the
 HierarchyViewer tool now lets you see start/end properties, layout direction,
-text direction, and text alignment for all the Views in the hierarchy,</p>
+text direction, and text alignment for all the Views in the hierarchy.</p>
 
 
 <h2 id="42-intl">Enhancements for international languages</h2>
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 3a83d12..7d99fec 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1849,7 +1849,7 @@
      * @hide
      */
     public int getTextRunCursor(String text, int contextStart, int contextEnd,
-            int flags, int offset, int cursorOpt) {
+            int offset, int cursorOpt) {
         if (((contextStart | contextEnd | offset | (contextEnd - contextStart)
                 | (offset - contextStart) | (contextEnd - offset)
                 | (text.length() - contextEnd) | cursorOpt) < 0)
diff --git a/graphics/java/android/renderscript/Element.java b/graphics/java/android/renderscript/Element.java
index a79ce17..f6c07d1 100644
--- a/graphics/java/android/renderscript/Element.java
+++ b/graphics/java/android/renderscript/Element.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2012 The Android Open Source Project
+ * Copyright (C) 2013 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -906,7 +906,8 @@
               dk == DataKind.PIXEL_LA ||
               dk == DataKind.PIXEL_RGB ||
               dk == DataKind.PIXEL_RGBA ||
-              dk == DataKind.PIXEL_DEPTH)) {
+              dk == DataKind.PIXEL_DEPTH ||
+              dk == DataKind.PIXEL_YUV)) {
             throw new RSIllegalArgumentException("Unsupported DataKind");
         }
         if (!(dt == DataType.UNSIGNED_8 ||
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index c3ddd32..10f4daa 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -801,6 +801,9 @@
     Sampler mSampler_WRAP_NEAREST;
     Sampler mSampler_WRAP_LINEAR;
     Sampler mSampler_WRAP_LINEAR_MIP_LINEAR;
+    Sampler mSampler_MIRRORED_REPEAT_NEAREST;
+    Sampler mSampler_MIRRORED_REPEAT_LINEAR;
+    Sampler mSampler_MIRRORED_REPEAT_LINEAR_MIP_LINEAR;
 
     ProgramStore mProgramStore_BLEND_NONE_DEPTH_TEST;
     ProgramStore mProgramStore_BLEND_NONE_DEPTH_NO_DEPTH;
diff --git a/graphics/java/android/renderscript/Sampler.java b/graphics/java/android/renderscript/Sampler.java
index 0df1012..057e9b5 100644
--- a/graphics/java/android/renderscript/Sampler.java
+++ b/graphics/java/android/renderscript/Sampler.java
@@ -39,7 +39,8 @@
         LINEAR_MIP_LINEAR (2),
         LINEAR_MIP_NEAREST (5),
         WRAP (3),
-        CLAMP (4);
+        CLAMP (4),
+        MIRRORED_REPEAT (6);
 
         int mID;
         Value(int id) {
@@ -134,8 +135,8 @@
     }
 
     /**
-     * Retrieve a sampler with ag set to linear, min linear mipmap linear, and
-     * to and wrap modes set to clamp.
+     * Retrieve a sampler with mag set to linear, min linear mipmap linear, and
+     * wrap modes set to clamp.
      *
      * @param rs Context to which the sampler will belong.
      *
@@ -174,7 +175,7 @@
     }
 
     /**
-     * Retrieve a sampler with min and mag set to nearest and wrap modes set to
+     * Retrieve a sampler with min and mag set to linear and wrap modes set to
      * wrap.
      *
      * @param rs Context to which the sampler will belong.
@@ -194,8 +195,8 @@
     }
 
     /**
-     * Retrieve a sampler with ag set to linear, min linear mipmap linear, and
-     * to and wrap modes set to wrap.
+     * Retrieve a sampler with mag set to linear, min linear mipmap linear, and
+     * wrap modes set to wrap.
      *
      * @param rs Context to which the sampler will belong.
      *
@@ -213,6 +214,65 @@
         return rs.mSampler_WRAP_LINEAR_MIP_LINEAR;
     }
 
+    /**
+     * Retrieve a sampler with min and mag set to nearest and wrap modes set to
+     * mirrored repeat.
+     *
+     * @param rs Context to which the sampler will belong.
+     *
+     * @return Sampler
+     */
+    public static Sampler MIRRORED_REPEAT_NEAREST(RenderScript rs) {
+        if(rs.mSampler_MIRRORED_REPEAT_NEAREST == null) {
+            Builder b = new Builder(rs);
+            b.setMinification(Value.NEAREST);
+            b.setMagnification(Value.NEAREST);
+            b.setWrapS(Value.MIRRORED_REPEAT);
+            b.setWrapT(Value.MIRRORED_REPEAT);
+            rs.mSampler_MIRRORED_REPEAT_NEAREST = b.create();
+        }
+        return rs.mSampler_MIRRORED_REPEAT_NEAREST;
+    }
+
+    /**
+     * Retrieve a sampler with min and mag set to linear and wrap modes set to
+     * mirrored repeat.
+     *
+     * @param rs Context to which the sampler will belong.
+     *
+     * @return Sampler
+     */
+    public static Sampler MIRRORED_REPEAT_LINEAR(RenderScript rs) {
+        if(rs.mSampler_MIRRORED_REPEAT_LINEAR == null) {
+            Builder b = new Builder(rs);
+            b.setMinification(Value.LINEAR);
+            b.setMagnification(Value.LINEAR);
+            b.setWrapS(Value.MIRRORED_REPEAT);
+            b.setWrapT(Value.MIRRORED_REPEAT);
+            rs.mSampler_MIRRORED_REPEAT_LINEAR = b.create();
+        }
+        return rs.mSampler_MIRRORED_REPEAT_LINEAR;
+    }
+
+    /**
+     * Retrieve a sampler with min and mag set to linear and wrap modes set to
+     * mirrored repeat.
+     *
+     * @param rs Context to which the sampler will belong.
+     *
+     * @return Sampler
+     */
+    public static Sampler MIRRORED_REPEAT_LINEAR_MIP_LINEAR(RenderScript rs) {
+        if(rs.mSampler_MIRRORED_REPEAT_LINEAR_MIP_LINEAR == null) {
+            Builder b = new Builder(rs);
+            b.setMinification(Value.LINEAR_MIP_LINEAR);
+            b.setMagnification(Value.LINEAR);
+            b.setWrapS(Value.MIRRORED_REPEAT);
+            b.setWrapT(Value.MIRRORED_REPEAT);
+            rs.mSampler_MIRRORED_REPEAT_LINEAR_MIP_LINEAR = b.create();
+        }
+        return rs.mSampler_MIRRORED_REPEAT_LINEAR_MIP_LINEAR;
+    }
 
     /**
      * Builder for creating non-standard samplers.  Usefull if mix and match of
@@ -258,7 +318,7 @@
         }
 
         public void setWrapS(Value v) {
-            if (v == Value.WRAP || v == Value.CLAMP) {
+            if (v == Value.WRAP || v == Value.CLAMP || v == Value.MIRRORED_REPEAT) {
                 mWrapS = v;
             } else {
                 throw new IllegalArgumentException("Invalid value");
@@ -266,7 +326,7 @@
         }
 
         public void setWrapT(Value v) {
-            if (v == Value.WRAP || v == Value.CLAMP) {
+            if (v == Value.WRAP || v == Value.CLAMP || v == Value.MIRRORED_REPEAT) {
                 mWrapT = v;
             } else {
                 throw new IllegalArgumentException("Invalid value");
diff --git a/graphics/java/android/renderscript/Type.java b/graphics/java/android/renderscript/Type.java
index d1446f6..9507030 100644
--- a/graphics/java/android/renderscript/Type.java
+++ b/graphics/java/android/renderscript/Type.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2013 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index 8830685..5e631af 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -43,7 +43,7 @@
 #include <rsEnv.h>
 #include <gui/Surface.h>
 #include <gui/GLConsumer.h>
-#include <gui/SurfaceTextureClient.h>
+#include <gui/Surface.h>
 #include <android_runtime/android_graphics_SurfaceTexture.h>
 
 //#define LOG_API ALOGE
@@ -247,7 +247,7 @@
 
     } else {
         st = SurfaceTexture_getSurfaceTexture(_env, sur);
-        window = new SurfaceTextureClient(st->getBufferQueue());
+        window = new Surface(st->getBufferQueue());
     }
 
     rsContextSetSurface(con, width, height, window.get());
diff --git a/include/android_runtime/android_view_Surface.h b/include/android_runtime/android_view_Surface.h
index b56aff8..53e8b49 100644
--- a/include/android_runtime/android_view_Surface.h
+++ b/include/android_runtime/android_view_Surface.h
@@ -37,7 +37,7 @@
 extern sp<Surface> android_view_Surface_getSurface(JNIEnv* env, jobject surfaceObj);
 
 /* Creates a Surface from an IGraphicBufferProducer. */
-extern jobject android_view_Surface_createFromISurfaceTexture(JNIEnv* env,
+extern jobject android_view_Surface_createFromIGraphicBufferProducer(JNIEnv* env,
         const sp<IGraphicBufferProducer>& bufferProducer);
 
 } // namespace android
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 6bc7aef..5f2a4d5 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -11,6 +11,8 @@
 		FontRenderer.cpp \
 		GammaFontRenderer.cpp \
 		Caches.cpp \
+		DisplayList.cpp \
+		DeferredDisplayList.cpp \
 		DisplayListLogBuffer.cpp \
 		DisplayListRenderer.cpp \
 		Dither.cpp \
diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h
index 20eb5e1..9a6494f 100644
--- a/libs/hwui/Debug.h
+++ b/libs/hwui/Debug.h
@@ -74,6 +74,9 @@
 // Turn on to enable additional debugging in the font renderers
 #define DEBUG_FONT_RENDERER 0
 
+// Turn on to log draw operation batching and deferral information
+#define DEBUG_DEFER 0
+
 // Turn on to dump display list state
 #define DEBUG_DISPLAY_LIST 0
 
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
new file mode 100644
index 0000000..8962964
--- /dev/null
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+#define ATRACE_TAG ATRACE_TAG_VIEW
+
+#include <utils/Trace.h>
+
+#include "Debug.h"
+#include "DisplayListOp.h"
+#include "OpenGLRenderer.h"
+
+#if DEBUG_DEFER
+    #define DEFER_LOGD(...) ALOGD(__VA_ARGS__)
+#else
+    #define DEFER_LOGD(...)
+#endif
+
+namespace android {
+namespace uirenderer {
+
+class DrawOpBatch {
+public:
+    DrawOpBatch() {
+        mOps.clear();
+    }
+
+    ~DrawOpBatch() {
+        mOps.clear();
+    }
+
+    void add(DrawOp* op) {
+        // NOTE: ignore empty bounds special case, since we don't merge across those ops
+        mBounds.unionWith(op->state.mBounds);
+        mOps.add(op);
+    }
+
+    bool intersects(Rect& rect) {
+        if (!rect.intersects(mBounds)) return false;
+        for (unsigned int i = 0; i < mOps.size(); i++) {
+            if (rect.intersects(mOps[i]->state.mBounds)) {
+#if DEBUG_DEFER
+                DEFER_LOGD("op intersects with op %p with bounds %f %f %f %f:", mOps[i],
+                        mOps[i]->state.mBounds.left, mOps[i]->state.mBounds.top,
+                        mOps[i]->state.mBounds.right, mOps[i]->state.mBounds.bottom);
+                mOps[i]->output(2);
+#endif
+                return true;
+            }
+        }
+        return false;
+    }
+
+    Vector<DrawOp*> mOps;
+private:
+    Rect mBounds;
+};
+
+void DeferredDisplayList::clear() {
+    for (int i = 0; i < kOpBatch_Count; i++) {
+        mBatchIndices[i] = -1;
+    }
+    for (unsigned int i = 0; i < mBatches.size(); i++) {
+        delete mBatches[i];
+    }
+    mBatches.clear();
+}
+
+void DeferredDisplayList::add(DrawOp* op, bool disallowReorder) {
+    if (CC_UNLIKELY(disallowReorder)) {
+        if (!mBatches.isEmpty()) {
+            mBatches[0]->add(op);
+            return;
+        }
+        DrawOpBatch* b = new DrawOpBatch();
+        b->add(op);
+        mBatches.add(b);
+        return;
+    }
+
+    // disallowReorder isn't set, so find the latest batch of the new op's type, and try to merge
+    // the new op into it
+    DrawOpBatch* targetBatch = NULL;
+    int batchId = op->getBatchId();
+
+    if (!mBatches.isEmpty()) {
+        if (op->state.mBounds.isEmpty()) {
+            // don't know the bounds for op, so add to last batch and start from scratch on next op
+            mBatches.top()->add(op);
+            for (int i = 0; i < kOpBatch_Count; i++) {
+                mBatchIndices[i] = -1;
+            }
+#if DEBUG_DEFER
+            DEFER_LOGD("Warning: Encountered op with empty bounds, resetting batches");
+            op->output(2);
+#endif
+            return;
+        }
+
+        if (batchId >= 0 && mBatchIndices[batchId] != -1) {
+            int targetIndex = mBatchIndices[batchId];
+            targetBatch = mBatches[targetIndex];
+            // iterate back toward target to see if anything drawn since should overlap the new op
+            for (int i = mBatches.size() - 1; i > targetIndex; i--) {
+                DrawOpBatch* overBatch = mBatches[i];
+                if (overBatch->intersects(op->state.mBounds)) {
+                    targetBatch = NULL;
+#if DEBUG_DEFER
+                    DEFER_LOGD("op couldn't join batch %d, was intersected by batch %d",
+                            targetIndex, i);
+                    op->output(2);
+#endif
+                    break;
+                }
+            }
+        }
+    }
+    if (!targetBatch) {
+        targetBatch = new DrawOpBatch();
+        mBatches.add(targetBatch);
+        if (batchId >= 0) {
+            mBatchIndices[batchId] = mBatches.size() - 1;
+        }
+    }
+    targetBatch->add(op);
+}
+
+status_t DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty, int32_t flags,
+        uint32_t level) {
+    ATRACE_CALL();
+    status_t status = DrawGlInfo::kStatusDone;
+
+    if (isEmpty()) return status; // nothing to flush
+
+    DEFER_LOGD("--flushing");
+    DrawModifiers restoreDrawModifiers = renderer.getDrawModifiers();
+    int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+    int opCount = 0;
+    for (unsigned int i = 0; i < mBatches.size(); i++) {
+        DrawOpBatch* batch = mBatches[i];
+        for (unsigned int j = 0; j < batch->mOps.size(); j++) {
+            DrawOp* op = batch->mOps[j];
+
+            renderer.restoreDisplayState(op->state);
+
+#if DEBUG_DEFER
+            op->output(2);
+#endif
+            status |= op->applyDraw(renderer, dirty, level,
+                    op->state.mMultipliedAlpha >= 0, op->state.mMultipliedAlpha);
+            opCount++;
+        }
+    }
+
+    DEFER_LOGD("--flushed, drew %d batches (total %d ops)", mBatches.size(), opCount);
+    renderer.restoreToCount(restoreTo);
+    renderer.setDrawModifiers(restoreDrawModifiers);
+    clear();
+    return status;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
new file mode 100644
index 0000000..4fcb297
--- /dev/null
+++ b/libs/hwui/DeferredDisplayList.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_DEFERRED_DISPLAY_LIST_H
+#define ANDROID_HWUI_DEFERRED_DISPLAY_LIST_H
+
+#include <utils/Errors.h>
+#include <utils/Vector.h>
+
+#include "Matrix.h"
+#include "Rect.h"
+
+namespace android {
+namespace uirenderer {
+
+class DrawOp;
+class DrawOpBatch;
+class OpenGLRenderer;
+class SkiaShader;
+
+class DeferredDisplayList {
+public:
+    DeferredDisplayList() { clear(); }
+    ~DeferredDisplayList() { clear(); }
+
+    enum OpBatchId {
+        kOpBatch_None = -1, // Don't batch
+        kOpBatch_Bitmap,
+        kOpBatch_Patch,
+        kOpBatch_AlphaVertices,
+        kOpBatch_Vertices,
+        kOpBatch_AlphaMaskTexture,
+        kOpBatch_Text,
+        kOpBatch_ColorText,
+
+        kOpBatch_Count, // Add other batch ids before this
+    };
+
+    bool isEmpty() { return mBatches.isEmpty(); }
+
+    /**
+     * Plays back all of the draw ops recorded into batches to the renderer.
+     * Adjusts the state of the renderer as necessary, and restores it when complete
+     */
+    status_t flush(OpenGLRenderer& renderer, Rect& dirty, int32_t flags,
+            uint32_t level);
+
+    /**
+     * Add a draw op into the DeferredDisplayList, reordering as needed (for performance) if
+     * disallowReorder is false, respecting draw order when overlaps occur
+     */
+    void add(DrawOp* op, bool disallowReorder);
+
+private:
+    void clear();
+
+
+    Vector<DrawOpBatch*> mBatches;
+    int mBatchIndices[kOpBatch_Count];
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_DEFERRED_DISPLAY_LIST_H
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
new file mode 100644
index 0000000..8aac628
--- /dev/null
+++ b/libs/hwui/DisplayList.cpp
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Debug.h"
+#include "DisplayList.h"
+#include "DisplayListOp.h"
+#include "DisplayListLogBuffer.h"
+
+namespace android {
+namespace uirenderer {
+
+void DisplayList::outputLogBuffer(int fd) {
+    DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
+    if (logBuffer.isEmpty()) {
+        return;
+    }
+
+    FILE *file = fdopen(fd, "a");
+
+    fprintf(file, "\nRecent DisplayList operations\n");
+    logBuffer.outputCommands(file);
+
+    String8 cachesLog;
+    Caches::getInstance().dumpMemoryUsage(cachesLog);
+    fprintf(file, "\nCaches:\n%s", cachesLog.string());
+    fprintf(file, "\n");
+
+    fflush(file);
+}
+
+DisplayList::DisplayList(const DisplayListRenderer& recorder) :
+    mTransformMatrix(NULL), mTransformCamera(NULL), mTransformMatrix3D(NULL),
+    mStaticMatrix(NULL), mAnimationMatrix(NULL) {
+
+    initFromDisplayListRenderer(recorder);
+}
+
+DisplayList::~DisplayList() {
+    clearResources();
+}
+
+void DisplayList::destroyDisplayListDeferred(DisplayList* displayList) {
+    if (displayList) {
+        DISPLAY_LIST_LOGD("Deferring display list destruction");
+        Caches::getInstance().deleteDisplayListDeferred(displayList);
+    }
+}
+
+void DisplayList::clearResources() {
+    mDisplayListData = NULL;
+    delete mTransformMatrix;
+    delete mTransformCamera;
+    delete mTransformMatrix3D;
+    delete mStaticMatrix;
+    delete mAnimationMatrix;
+
+    mTransformMatrix = NULL;
+    mTransformCamera = NULL;
+    mTransformMatrix3D = NULL;
+    mStaticMatrix = NULL;
+    mAnimationMatrix = NULL;
+
+    Caches& caches = Caches::getInstance();
+    caches.unregisterFunctors(mFunctorCount);
+    caches.resourceCache.lock();
+
+    for (size_t i = 0; i < mBitmapResources.size(); i++) {
+        caches.resourceCache.decrementRefcountLocked(mBitmapResources.itemAt(i));
+    }
+
+    for (size_t i = 0; i < mOwnedBitmapResources.size(); i++) {
+        SkBitmap* bitmap = mOwnedBitmapResources.itemAt(i);
+        caches.resourceCache.decrementRefcountLocked(bitmap);
+        caches.resourceCache.destructorLocked(bitmap);
+    }
+
+    for (size_t i = 0; i < mFilterResources.size(); i++) {
+        caches.resourceCache.decrementRefcountLocked(mFilterResources.itemAt(i));
+    }
+
+    for (size_t i = 0; i < mShaders.size(); i++) {
+        caches.resourceCache.decrementRefcountLocked(mShaders.itemAt(i));
+        caches.resourceCache.destructorLocked(mShaders.itemAt(i));
+    }
+
+    for (size_t i = 0; i < mSourcePaths.size(); i++) {
+        caches.resourceCache.decrementRefcountLocked(mSourcePaths.itemAt(i));
+    }
+
+    for (size_t i = 0; i < mLayers.size(); i++) {
+        caches.resourceCache.decrementRefcountLocked(mLayers.itemAt(i));
+    }
+
+    caches.resourceCache.unlock();
+
+    for (size_t i = 0; i < mPaints.size(); i++) {
+        delete mPaints.itemAt(i);
+    }
+
+    for (size_t i = 0; i < mRegions.size(); i++) {
+        delete mRegions.itemAt(i);
+    }
+
+    for (size_t i = 0; i < mPaths.size(); i++) {
+        SkPath* path = mPaths.itemAt(i);
+        caches.pathCache.remove(path);
+        delete path;
+    }
+
+    for (size_t i = 0; i < mMatrices.size(); i++) {
+        delete mMatrices.itemAt(i);
+    }
+
+    mBitmapResources.clear();
+    mOwnedBitmapResources.clear();
+    mFilterResources.clear();
+    mShaders.clear();
+    mSourcePaths.clear();
+    mPaints.clear();
+    mRegions.clear();
+    mPaths.clear();
+    mMatrices.clear();
+    mLayers.clear();
+}
+
+void DisplayList::reset() {
+    clearResources();
+    init();
+}
+
+void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorder, bool reusing) {
+    if (reusing) {
+        // re-using display list - clear out previous allocations
+        clearResources();
+    }
+
+    init();
+
+    mDisplayListData = recorder.getDisplayListData();
+    mSize = mDisplayListData->allocator.usedSize();
+
+    if (mSize == 0) {
+        return;
+    }
+
+    mFunctorCount = recorder.getFunctorCount();
+
+    Caches& caches = Caches::getInstance();
+    caches.registerFunctors(mFunctorCount);
+    caches.resourceCache.lock();
+
+    const Vector<SkBitmap*>& bitmapResources = recorder.getBitmapResources();
+    for (size_t i = 0; i < bitmapResources.size(); i++) {
+        SkBitmap* resource = bitmapResources.itemAt(i);
+        mBitmapResources.add(resource);
+        caches.resourceCache.incrementRefcountLocked(resource);
+    }
+
+    const Vector<SkBitmap*> &ownedBitmapResources = recorder.getOwnedBitmapResources();
+    for (size_t i = 0; i < ownedBitmapResources.size(); i++) {
+        SkBitmap* resource = ownedBitmapResources.itemAt(i);
+        mOwnedBitmapResources.add(resource);
+        caches.resourceCache.incrementRefcountLocked(resource);
+    }
+
+    const Vector<SkiaColorFilter*>& filterResources = recorder.getFilterResources();
+    for (size_t i = 0; i < filterResources.size(); i++) {
+        SkiaColorFilter* resource = filterResources.itemAt(i);
+        mFilterResources.add(resource);
+        caches.resourceCache.incrementRefcountLocked(resource);
+    }
+
+    const Vector<SkiaShader*>& shaders = recorder.getShaders();
+    for (size_t i = 0; i < shaders.size(); i++) {
+        SkiaShader* resource = shaders.itemAt(i);
+        mShaders.add(resource);
+        caches.resourceCache.incrementRefcountLocked(resource);
+    }
+
+    const SortedVector<SkPath*>& sourcePaths = recorder.getSourcePaths();
+    for (size_t i = 0; i < sourcePaths.size(); i++) {
+        mSourcePaths.add(sourcePaths.itemAt(i));
+        caches.resourceCache.incrementRefcountLocked(sourcePaths.itemAt(i));
+    }
+
+    const Vector<Layer*>& layers = recorder.getLayers();
+    for (size_t i = 0; i < layers.size(); i++) {
+        mLayers.add(layers.itemAt(i));
+        caches.resourceCache.incrementRefcountLocked(layers.itemAt(i));
+    }
+
+    caches.resourceCache.unlock();
+
+    mPaints.appendVector(recorder.getPaints());
+    mRegions.appendVector(recorder.getRegions());
+    mPaths.appendVector(recorder.getPaths());
+    mMatrices.appendVector(recorder.getMatrices());
+}
+
+void DisplayList::init() {
+    mSize = 0;
+    mIsRenderable = true;
+    mFunctorCount = 0;
+    mLeft = 0;
+    mTop = 0;
+    mRight = 0;
+    mBottom = 0;
+    mClipChildren = true;
+    mAlpha = 1;
+    mMultipliedAlpha = 255;
+    mHasOverlappingRendering = true;
+    mTranslationX = 0;
+    mTranslationY = 0;
+    mRotation = 0;
+    mRotationX = 0;
+    mRotationY= 0;
+    mScaleX = 1;
+    mScaleY = 1;
+    mPivotX = 0;
+    mPivotY = 0;
+    mCameraDistance = 0;
+    mMatrixDirty = false;
+    mMatrixFlags = 0;
+    mPrevWidth = -1;
+    mPrevHeight = -1;
+    mWidth = 0;
+    mHeight = 0;
+    mPivotExplicitlySet = false;
+    mCaching = false;
+}
+
+size_t DisplayList::getSize() {
+    return mSize;
+}
+
+/**
+ * This function is a simplified version of replay(), where we simply retrieve and log the
+ * display list. This function should remain in sync with the replay() function.
+ */
+void DisplayList::output(uint32_t level) {
+    ALOGD("%*sStart display list (%p, %s, render=%d)", level * 2, "", this,
+            mName.string(), isRenderable());
+
+    ALOGD("%*s%s %d", level * 2, "", "Save", SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+    outputViewProperties(level);
+    int flags = DisplayListOp::kOpLogFlag_Recurse;
+    for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
+        mDisplayListData->displayListOps[i]->output(level, flags);
+    }
+    ALOGD("%*sDone (%p, %s)", level * 2, "", this, mName.string());
+}
+
+void DisplayList::updateMatrix() {
+    if (mMatrixDirty) {
+        if (!mTransformMatrix) {
+            mTransformMatrix = new SkMatrix();
+        }
+        if (mMatrixFlags == 0 || mMatrixFlags == TRANSLATION) {
+            mTransformMatrix->reset();
+        } else {
+            if (!mPivotExplicitlySet) {
+                if (mWidth != mPrevWidth || mHeight != mPrevHeight) {
+                    mPrevWidth = mWidth;
+                    mPrevHeight = mHeight;
+                    mPivotX = mPrevWidth / 2;
+                    mPivotY = mPrevHeight / 2;
+                }
+            }
+            if ((mMatrixFlags & ROTATION_3D) == 0) {
+                mTransformMatrix->setTranslate(mTranslationX, mTranslationY);
+                mTransformMatrix->preRotate(mRotation, mPivotX, mPivotY);
+                mTransformMatrix->preScale(mScaleX, mScaleY, mPivotX, mPivotY);
+            } else {
+                if (!mTransformCamera) {
+                    mTransformCamera = new Sk3DView();
+                    mTransformMatrix3D = new SkMatrix();
+                }
+                mTransformMatrix->reset();
+                mTransformCamera->save();
+                mTransformMatrix->preScale(mScaleX, mScaleY, mPivotX, mPivotY);
+                mTransformCamera->rotateX(mRotationX);
+                mTransformCamera->rotateY(mRotationY);
+                mTransformCamera->rotateZ(-mRotation);
+                mTransformCamera->getMatrix(mTransformMatrix3D);
+                mTransformMatrix3D->preTranslate(-mPivotX, -mPivotY);
+                mTransformMatrix3D->postTranslate(mPivotX + mTranslationX,
+                        mPivotY + mTranslationY);
+                mTransformMatrix->postConcat(*mTransformMatrix3D);
+                mTransformCamera->restore();
+            }
+        }
+        mMatrixDirty = false;
+    }
+}
+
+void DisplayList::outputViewProperties(uint32_t level) {
+    updateMatrix();
+    if (mLeft != 0 || mTop != 0) {
+        ALOGD("%*sTranslate (left, top) %d, %d", level * 2, "", mLeft, mTop);
+    }
+    if (mStaticMatrix) {
+        ALOGD("%*sConcatMatrix (static) %p: " MATRIX_STRING,
+                level * 2, "", mStaticMatrix, MATRIX_ARGS(mStaticMatrix));
+    }
+    if (mAnimationMatrix) {
+        ALOGD("%*sConcatMatrix (animation) %p: " MATRIX_STRING,
+                level * 2, "", mAnimationMatrix, MATRIX_ARGS(mStaticMatrix));
+    }
+    if (mMatrixFlags != 0) {
+        if (mMatrixFlags == TRANSLATION) {
+            ALOGD("%*sTranslate %f, %f", level * 2, "", mTranslationX, mTranslationY);
+        } else {
+            ALOGD("%*sConcatMatrix %p: " MATRIX_STRING,
+                    level * 2, "", mTransformMatrix, MATRIX_ARGS(mTransformMatrix));
+        }
+    }
+    if (mAlpha < 1 && !mCaching) {
+        if (!mHasOverlappingRendering) {
+            ALOGD("%*sSetAlpha %.2f", level * 2, "", mAlpha);
+        } else {
+            int flags = SkCanvas::kHasAlphaLayer_SaveFlag;
+            if (mClipChildren) {
+                flags |= SkCanvas::kClipToLayer_SaveFlag;
+            }
+            ALOGD("%*sSaveLayerAlpha %.2f, %.2f, %.2f, %.2f, %d, 0x%x", level * 2, "",
+                    (float) 0, (float) 0, (float) mRight - mLeft, (float) mBottom - mTop,
+                    mMultipliedAlpha, flags);
+        }
+    }
+    if (mClipChildren && !mCaching) {
+        ALOGD("%*sClipRect %.2f, %.2f, %.2f, %.2f", level * 2, "", 0.0f, 0.0f,
+                (float) mRight - mLeft, (float) mBottom - mTop);
+    }
+}
+
+void DisplayList::setViewProperties(OpenGLRenderer& renderer, uint32_t level) {
+#if DEBUG_DISPLAYLIST
+    outputViewProperties(level);
+#endif
+    updateMatrix();
+    if (mLeft != 0 || mTop != 0) {
+        renderer.translate(mLeft, mTop);
+    }
+    if (mStaticMatrix) {
+        renderer.concatMatrix(mStaticMatrix);
+    } else if (mAnimationMatrix) {
+        renderer.concatMatrix(mAnimationMatrix);
+    }
+    if (mMatrixFlags != 0) {
+        if (mMatrixFlags == TRANSLATION) {
+            renderer.translate(mTranslationX, mTranslationY);
+        } else {
+            renderer.concatMatrix(mTransformMatrix);
+        }
+    }
+    if (mAlpha < 1 && !mCaching) {
+        if (!mHasOverlappingRendering) {
+            renderer.setAlpha(mAlpha);
+        } else {
+            // TODO: should be able to store the size of a DL at record time and not
+            // have to pass it into this call. In fact, this information might be in the
+            // location/size info that we store with the new native transform data.
+            int flags = SkCanvas::kHasAlphaLayer_SaveFlag;
+            if (mClipChildren) {
+                flags |= SkCanvas::kClipToLayer_SaveFlag;
+            }
+            renderer.saveLayerAlpha(0, 0, mRight - mLeft, mBottom - mTop,
+                    mMultipliedAlpha, flags);
+        }
+    }
+    if (mClipChildren && !mCaching) {
+        renderer.clipRect(0, 0, mRight - mLeft, mBottom - mTop,
+                SkRegion::kIntersect_Op);
+    }
+}
+
+status_t DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, uint32_t level,
+        DeferredDisplayList* deferredList) {
+    status_t drawGlStatus = DrawGlInfo::kStatusDone;
+
+#if DEBUG_DISPLAY_LIST
+    Rect* clipRect = renderer.getClipRect();
+    DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), clipRect: %.0f, %.f, %.0f, %.0f",
+            (level+1)*2, "", this, mName.string(), clipRect->left, clipRect->top,
+            clipRect->right, clipRect->bottom);
+#endif
+
+    renderer.startMark(mName.string());
+
+    int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+    DISPLAY_LIST_LOGD("%*sSave %d %d", level * 2, "",
+            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);
+
+    if (mAlpha < 1 && !mCaching && CC_LIKELY(deferredList)) {
+        // flush before a saveLayerAlpha/setAlpha
+        // TODO: make this cleaner
+        drawGlStatus |= deferredList->flush(renderer, dirty, flags, level);
+    }
+    setViewProperties(renderer, level);
+
+    if (renderer.quickRejectNoScissor(0, 0, mWidth, mHeight)) {
+        DISPLAY_LIST_LOGD("%*sRestoreToCount %d", level * 2, "", restoreTo);
+        renderer.restoreToCount(restoreTo);
+        renderer.endMark();
+        return drawGlStatus;
+    }
+
+    DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
+    int saveCount = renderer.getSaveCount() - 1;
+    for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
+        DisplayListOp *op = mDisplayListData->displayListOps[i];
+#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
+        Caches::getInstance().eventMark(strlen(op->name()), op->name());
+#endif
+
+        if (deferredList) {
+            drawGlStatus |= op->replay(renderer, dirty, flags,
+                    saveCount, level, mCaching, mMultipliedAlpha, *deferredList);
+        } else {
+            drawGlStatus |= op->replay(renderer, dirty, flags,
+                    saveCount, level, mCaching, mMultipliedAlpha);
+        }
+        logBuffer.writeCommand(level, op->name());
+    }
+
+    DISPLAY_LIST_LOGD("%*sRestoreToCount %d", level * 2, "", restoreTo);
+    renderer.restoreToCount(restoreTo);
+    renderer.endMark();
+
+    DISPLAY_LIST_LOGD("%*sDone (%p, %s), returning %d", (level + 1) * 2, "", this, mName.string(),
+            drawGlStatus);
+
+    if (!level && CC_LIKELY(deferredList)) {
+        drawGlStatus |= deferredList->flush(renderer, dirty, flags, level);
+    }
+
+    return drawGlStatus;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
new file mode 100644
index 0000000..d06827d
--- /dev/null
+++ b/libs/hwui/DisplayList.h
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_DISPLAY_LIST_H
+#define ANDROID_HWUI_DISPLAY_LIST_H
+
+#include <SkCamera.h>
+#include <SkMatrix.h>
+
+#include <utils/RefBase.h>
+#include <utils/SortedVector.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+#include <cutils/compiler.h>
+
+#include "utils/LinearAllocator.h"
+
+#include "Debug.h"
+
+#define TRANSLATION 0x0001
+#define ROTATION    0x0002
+#define ROTATION_3D 0x0004
+#define SCALE       0x0008
+#define PIVOT       0x0010
+
+class SkBitmap;
+class SkPaint;
+class SkPath;
+class SkRegion;
+
+namespace android {
+namespace uirenderer {
+
+class DeferredDisplayList;
+class DisplayListOp;
+class DisplayListRenderer;
+class OpenGLRenderer;
+class Rect;
+class Layer;
+class SkiaColorFilter;
+class SkiaShader;
+
+/**
+ * Refcounted structure that holds data used in display list stream
+ */
+class DisplayListData: public LightRefBase<DisplayListData> {
+public:
+    LinearAllocator allocator;
+    Vector<DisplayListOp*> displayListOps;
+};
+
+/**
+ * Replays recorded drawing commands.
+ */
+class DisplayList {
+public:
+    DisplayList(const DisplayListRenderer& recorder);
+    ANDROID_API ~DisplayList();
+
+    // See flags defined in DisplayList.java
+    enum ReplayFlag {
+        kReplayFlag_ClipChildren = 0x1
+    };
+
+    void setViewProperties(OpenGLRenderer& renderer, uint32_t level);
+    void outputViewProperties(uint32_t level);
+
+    ANDROID_API size_t getSize();
+    ANDROID_API static void destroyDisplayListDeferred(DisplayList* displayList);
+    ANDROID_API static void outputLogBuffer(int fd);
+
+    void initFromDisplayListRenderer(const DisplayListRenderer& recorder, bool reusing = false);
+
+    status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, uint32_t level = 0,
+            DeferredDisplayList* deferredList = NULL);
+
+    void output(uint32_t level = 0);
+
+    ANDROID_API void reset();
+
+    void setRenderable(bool renderable) {
+        mIsRenderable = renderable;
+    }
+
+    bool isRenderable() const {
+        return mIsRenderable;
+    }
+
+    void setName(const char* name) {
+        if (name) {
+            mName.setTo(name);
+        }
+    }
+
+    void setClipChildren(bool clipChildren) {
+        mClipChildren = clipChildren;
+    }
+
+    void setStaticMatrix(SkMatrix* matrix) {
+        delete mStaticMatrix;
+        mStaticMatrix = new SkMatrix(*matrix);
+    }
+
+    void setAnimationMatrix(SkMatrix* matrix) {
+        delete mAnimationMatrix;
+        if (matrix) {
+            mAnimationMatrix = new SkMatrix(*matrix);
+        } else {
+            mAnimationMatrix = NULL;
+        }
+    }
+
+    void setAlpha(float alpha) {
+        alpha = fminf(1.0f, fmaxf(0.0f, alpha));
+        if (alpha != mAlpha) {
+            mAlpha = alpha;
+            mMultipliedAlpha = (int) (255 * alpha);
+        }
+    }
+
+    void setHasOverlappingRendering(bool hasOverlappingRendering) {
+        mHasOverlappingRendering = hasOverlappingRendering;
+    }
+
+    void setTranslationX(float translationX) {
+        if (translationX != mTranslationX) {
+            mTranslationX = translationX;
+            mMatrixDirty = true;
+            if (mTranslationX == 0.0f && mTranslationY == 0.0f) {
+                mMatrixFlags &= ~TRANSLATION;
+            } else {
+                mMatrixFlags |= TRANSLATION;
+            }
+        }
+    }
+
+    void setTranslationY(float translationY) {
+        if (translationY != mTranslationY) {
+            mTranslationY = translationY;
+            mMatrixDirty = true;
+            if (mTranslationX == 0.0f && mTranslationY == 0.0f) {
+                mMatrixFlags &= ~TRANSLATION;
+            } else {
+                mMatrixFlags |= TRANSLATION;
+            }
+        }
+    }
+
+    void setRotation(float rotation) {
+        if (rotation != mRotation) {
+            mRotation = rotation;
+            mMatrixDirty = true;
+            if (mRotation == 0.0f) {
+                mMatrixFlags &= ~ROTATION;
+            } else {
+                mMatrixFlags |= ROTATION;
+            }
+        }
+    }
+
+    void setRotationX(float rotationX) {
+        if (rotationX != mRotationX) {
+            mRotationX = rotationX;
+            mMatrixDirty = true;
+            if (mRotationX == 0.0f && mRotationY == 0.0f) {
+                mMatrixFlags &= ~ROTATION_3D;
+            } else {
+                mMatrixFlags |= ROTATION_3D;
+            }
+        }
+    }
+
+    void setRotationY(float rotationY) {
+        if (rotationY != mRotationY) {
+            mRotationY = rotationY;
+            mMatrixDirty = true;
+            if (mRotationX == 0.0f && mRotationY == 0.0f) {
+                mMatrixFlags &= ~ROTATION_3D;
+            } else {
+                mMatrixFlags |= ROTATION_3D;
+            }
+        }
+    }
+
+    void setScaleX(float scaleX) {
+        if (scaleX != mScaleX) {
+            mScaleX = scaleX;
+            mMatrixDirty = true;
+            if (mScaleX == 1.0f && mScaleY == 1.0f) {
+                mMatrixFlags &= ~SCALE;
+            } else {
+                mMatrixFlags |= SCALE;
+            }
+        }
+    }
+
+    void setScaleY(float scaleY) {
+        if (scaleY != mScaleY) {
+            mScaleY = scaleY;
+            mMatrixDirty = true;
+            if (mScaleX == 1.0f && mScaleY == 1.0f) {
+                mMatrixFlags &= ~SCALE;
+            } else {
+                mMatrixFlags |= SCALE;
+            }
+        }
+    }
+
+    void setPivotX(float pivotX) {
+        mPivotX = pivotX;
+        mMatrixDirty = true;
+        if (mPivotX == 0.0f && mPivotY == 0.0f) {
+            mMatrixFlags &= ~PIVOT;
+        } else {
+            mMatrixFlags |= PIVOT;
+        }
+        mPivotExplicitlySet = true;
+    }
+
+    void setPivotY(float pivotY) {
+        mPivotY = pivotY;
+        mMatrixDirty = true;
+        if (mPivotX == 0.0f && mPivotY == 0.0f) {
+            mMatrixFlags &= ~PIVOT;
+        } else {
+            mMatrixFlags |= PIVOT;
+        }
+        mPivotExplicitlySet = true;
+    }
+
+    void setCameraDistance(float distance) {
+        if (distance != mCameraDistance) {
+            mCameraDistance = distance;
+            mMatrixDirty = true;
+            if (!mTransformCamera) {
+                mTransformCamera = new Sk3DView();
+                mTransformMatrix3D = new SkMatrix();
+            }
+            mTransformCamera->setCameraLocation(0, 0, distance);
+        }
+    }
+
+    void setLeft(int left) {
+        if (left != mLeft) {
+            mLeft = left;
+            mWidth = mRight - mLeft;
+            if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
+                mMatrixDirty = true;
+            }
+        }
+    }
+
+    void setTop(int top) {
+        if (top != mTop) {
+            mTop = top;
+            mHeight = mBottom - mTop;
+            if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
+                mMatrixDirty = true;
+            }
+        }
+    }
+
+    void setRight(int right) {
+        if (right != mRight) {
+            mRight = right;
+            mWidth = mRight - mLeft;
+            if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
+                mMatrixDirty = true;
+            }
+        }
+    }
+
+    void setBottom(int bottom) {
+        if (bottom != mBottom) {
+            mBottom = bottom;
+            mHeight = mBottom - mTop;
+            if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
+                mMatrixDirty = true;
+            }
+        }
+    }
+
+    void setLeftTop(int left, int top) {
+        if (left != mLeft || top != mTop) {
+            mLeft = left;
+            mTop = top;
+            mWidth = mRight - mLeft;
+            mHeight = mBottom - mTop;
+            if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
+                mMatrixDirty = true;
+            }
+        }
+    }
+
+    void setLeftTopRightBottom(int left, int top, int right, int bottom) {
+        if (left != mLeft || top != mTop || right != mRight || bottom != mBottom) {
+            mLeft = left;
+            mTop = top;
+            mRight = right;
+            mBottom = bottom;
+            mWidth = mRight - mLeft;
+            mHeight = mBottom - mTop;
+            if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
+                mMatrixDirty = true;
+            }
+        }
+    }
+
+    void offsetLeftRight(int offset) {
+        if (offset != 0) {
+            mLeft += offset;
+            mRight += offset;
+            if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
+                mMatrixDirty = true;
+            }
+        }
+    }
+
+    void offsetTopBottom(int offset) {
+        if (offset != 0) {
+            mTop += offset;
+            mBottom += offset;
+            if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
+                mMatrixDirty = true;
+            }
+        }
+    }
+
+    void setCaching(bool caching) {
+        mCaching = caching;
+    }
+
+    int getWidth() {
+        return mWidth;
+    }
+
+    int getHeight() {
+        return mHeight;
+    }
+
+private:
+    void init();
+
+    void clearResources();
+
+    void updateMatrix();
+
+    class TextContainer {
+    public:
+        size_t length() const {
+            return mByteLength;
+        }
+
+        const char* text() const {
+            return (const char*) mText;
+        }
+
+        size_t mByteLength;
+        const char* mText;
+    };
+
+    Vector<SkBitmap*> mBitmapResources;
+    Vector<SkBitmap*> mOwnedBitmapResources;
+    Vector<SkiaColorFilter*> mFilterResources;
+
+    Vector<SkPaint*> mPaints;
+    Vector<SkPath*> mPaths;
+    SortedVector<SkPath*> mSourcePaths;
+    Vector<SkRegion*> mRegions;
+    Vector<SkMatrix*> mMatrices;
+    Vector<SkiaShader*> mShaders;
+    Vector<Layer*> mLayers;
+
+    sp<DisplayListData> mDisplayListData;
+
+    size_t mSize;
+
+    bool mIsRenderable;
+    uint32_t mFunctorCount;
+
+    String8 mName;
+
+    // View properties
+    bool mClipChildren;
+    float mAlpha;
+    int mMultipliedAlpha;
+    bool mHasOverlappingRendering;
+    float mTranslationX, mTranslationY;
+    float mRotation, mRotationX, mRotationY;
+    float mScaleX, mScaleY;
+    float mPivotX, mPivotY;
+    float mCameraDistance;
+    int mLeft, mTop, mRight, mBottom;
+    int mWidth, mHeight;
+    int mPrevWidth, mPrevHeight;
+    bool mPivotExplicitlySet;
+    bool mMatrixDirty;
+    bool mMatrixIsIdentity;
+    uint32_t mMatrixFlags;
+    SkMatrix* mTransformMatrix;
+    Sk3DView* mTransformCamera;
+    SkMatrix* mTransformMatrix3D;
+    SkMatrix* mStaticMatrix;
+    SkMatrix* mAnimationMatrix;
+    bool mCaching;
+}; // class DisplayList
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_OPENGL_RENDERER_H
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 6425b43..8e80647 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -19,7 +19,10 @@
 
 #include <SkXfermode.h>
 
+#include <private/hwui/DrawGlInfo.h>
+
 #include "OpenGLRenderer.h"
+#include "DeferredDisplayList.h"
 #include "DisplayListRenderer.h"
 #include "utils/LinearAllocator.h"
 
@@ -41,7 +44,6 @@
 #define OP_LOGS(s) OP_LOG("%s", s)
 #define OP_LOG(s, ...) ALOGD( "%*s" s, level * 2, "", __VA_ARGS__ )
 
-
 namespace android {
 namespace uirenderer {
 
@@ -72,11 +74,15 @@
         kOpLogFlag_JSON = 0x2 // TODO: add?
     };
 
-    //TODO: for draw batching, DrawOps should override a virtual sub-method, with
-    // DrawOps::apply deferring operations to a different list if possible
     virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, int saveCount,
             uint32_t level, bool caching, int multipliedAlpha) = 0;
 
+    // same as replay above, but draw operations will defer into the deferredList if possible
+    // NOTE: colorfilters, paintfilters, shaders, shadow, and complex clips prevent deferral
+    virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, int saveCount,
+            uint32_t level, bool caching, int multipliedAlpha,
+            DeferredDisplayList& deferredList) = 0;
+
     virtual void output(int level, uint32_t flags = 0) = 0;
 
     // NOTE: it would be nice to declare constants and overriding the implementation in each op to
@@ -96,7 +102,28 @@
         return DrawGlInfo::kStatusDone;
     }
 
+    /**
+     * State operations are applied directly to the renderer, but can cause the deferred drawing op
+     * list to flush
+     */
+    virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, int saveCount,
+            uint32_t level, bool caching, int multipliedAlpha, DeferredDisplayList& deferredList) {
+        status_t status = DrawGlInfo::kStatusDone;
+        if (requiresDrawOpFlush()) {
+            // will be setting renderer state that affects ops in deferredList, so flush list first
+            status |= deferredList.flush(renderer, dirty, flags, level);
+        }
+        applyState(renderer, saveCount);
+        return status;
+    }
+
     virtual void applyState(OpenGLRenderer& renderer, int saveCount) = 0;
+
+    /**
+     * Returns true if it affects renderer drawing state in such a way to break deferral
+     * see OpenGLRenderer::disallowDeferral()
+     */
+    virtual bool requiresDrawOpFlush() { return false; }
 };
 
 class DrawOp : public DisplayListOp {
@@ -113,6 +140,33 @@
         return applyDraw(renderer, dirty, level, caching, multipliedAlpha);
     }
 
+    /** Draw operations are stored in the deferredList with information necessary for playback */
+    virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, int saveCount,
+            uint32_t level, bool caching, int multipliedAlpha, DeferredDisplayList& deferredList) {
+        if (mQuickRejected && CC_LIKELY(flags & DisplayList::kReplayFlag_ClipChildren)) {
+            return DrawGlInfo::kStatusDone;
+        }
+
+        if (renderer.disallowDeferral()) {
+            // dispatch draw immediately, since the renderer's state is too complex for deferral
+            return applyDraw(renderer, dirty, level, caching, multipliedAlpha);
+        }
+
+        if (!caching) multipliedAlpha = -1;
+        state.mMultipliedAlpha = multipliedAlpha;
+        if (!getLocalBounds(state.mBounds)) {
+            // empty bounds signify bounds can't be calculated
+            state.mBounds.setEmpty();
+        }
+
+        if (!renderer.storeDisplayState(state)) {
+            // op wasn't quick-rejected, so defer
+            deferredList.add(this, renderer.disallowReorder());
+        }
+
+        return DrawGlInfo::kStatusDone;
+    }
+
     virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
             bool caching, int multipliedAlpha) = 0;
 
@@ -123,6 +177,19 @@
     void setQuickRejected(bool quickRejected) { mQuickRejected = quickRejected; }
     bool getQuickRejected() { return mQuickRejected; }
 
+    /** Batching disabled by default, turned on for individual ops */
+    virtual DeferredDisplayList::OpBatchId getBatchId() {
+        return DeferredDisplayList::kOpBatch_None;
+    }
+
+    float strokeWidthOutset() { return mPaint->getStrokeWidth() * 0.5f; }
+
+public:
+    /**
+     * Stores the relevant canvas state of the object between deferral and replay (if the canvas
+     * state supports being stored) See OpenGLRenderer::simpleClipAndState()
+     */
+    DeferredDisplayState state;
 protected:
     SkPaint* getPaint(OpenGLRenderer& renderer) {
         return renderer.filterPaint(mPaint);
@@ -189,6 +256,8 @@
     }
 
     virtual const char* name() { return "RestoreToCount"; }
+    // Note: don't have to return true for requiresDrawOpFlush - even though restore can create a
+    // complex clip, the clip and matrix are overridden by DeferredDisplayList::flush()
 
 private:
     int mCount;
@@ -209,6 +278,7 @@
     }
 
     virtual const char* name() { return "SaveLayer"; }
+    virtual bool requiresDrawOpFlush() { return true; }
 
 private:
     Rect mArea;
@@ -230,6 +300,8 @@
     }
 
     virtual const char* name() { return "SaveLayerAlpha"; }
+    virtual bool requiresDrawOpFlush() { return true; }
+
 private:
     Rect mArea;
     int mAlpha;
@@ -389,6 +461,7 @@
     }
 
     virtual const char* name() { return "ClipPath"; }
+    virtual bool requiresDrawOpFlush() { return true; }
 
 private:
     SkPath* mPath;
@@ -411,6 +484,7 @@
     }
 
     virtual const char* name() { return "ClipRegion"; }
+    virtual bool requiresDrawOpFlush() { return true; }
 
 private:
     SkRegion* mRegion;
@@ -580,6 +654,9 @@
     }
 
     virtual const char* name() { return "DrawBitmap"; }
+    virtual DeferredDisplayList::OpBatchId getBatchId() {
+        return DeferredDisplayList::kOpBatch_Bitmap;
+    }
 
 protected:
     SkBitmap* mBitmap;
@@ -604,6 +681,9 @@
     }
 
     virtual const char* name() { return "DrawBitmap"; }
+    virtual DeferredDisplayList::OpBatchId getBatchId() {
+        return DeferredDisplayList::kOpBatch_Bitmap;
+    }
 
 private:
     SkBitmap* mBitmap;
@@ -630,6 +710,9 @@
     }
 
     virtual const char* name() { return "DrawBitmapRect"; }
+    virtual DeferredDisplayList::OpBatchId getBatchId() {
+        return DeferredDisplayList::kOpBatch_Bitmap;
+    }
 
 private:
     SkBitmap* mBitmap;
@@ -652,6 +735,9 @@
     }
 
     virtual const char* name() { return "DrawBitmapData"; }
+    virtual DeferredDisplayList::OpBatchId getBatchId() {
+        return DeferredDisplayList::kOpBatch_Bitmap;
+    }
 };
 
 class DrawBitmapMeshOp : public DrawOp {
@@ -672,6 +758,9 @@
     }
 
     virtual const char* name() { return "DrawBitmapMesh"; }
+    virtual DeferredDisplayList::OpBatchId getBatchId() {
+        return DeferredDisplayList::kOpBatch_Bitmap;
+    }
 
 private:
     SkBitmap* mBitmap;
@@ -706,6 +795,9 @@
     }
 
     virtual const char* name() { return "DrawPatch"; }
+    virtual DeferredDisplayList::OpBatchId getBatchId() {
+        return DeferredDisplayList::kOpBatch_Patch;
+    }
 
 private:
     SkBitmap* mBitmap;
@@ -746,15 +838,21 @@
             : DrawBoundedOp(left, top, right, bottom, paint) {};
 
     bool getLocalBounds(Rect& localBounds) {
+        localBounds.set(mLocalBounds);
         if (mPaint && mPaint->getStyle() != SkPaint::kFill_Style) {
-            float outset = mPaint->getStrokeWidth() * 0.5f;
-            localBounds.set(mLocalBounds.left - outset, mLocalBounds.top - outset,
-                    mLocalBounds.right + outset, mLocalBounds.bottom + outset);
-        } else {
-            localBounds.set(mLocalBounds);
+            localBounds.outset(strokeWidthOutset());
         }
         return true;
     }
+
+    virtual DeferredDisplayList::OpBatchId getBatchId() {
+        if (mPaint->getPathEffect()) {
+            return DeferredDisplayList::kOpBatch_AlphaMaskTexture;
+        }
+        return mPaint->isAntiAlias() ?
+                DeferredDisplayList::kOpBatch_AlphaVertices :
+                DeferredDisplayList::kOpBatch_Vertices;
+    }
 };
 
 class DrawRectOp : public DrawStrokableOp {
@@ -791,6 +889,10 @@
 
     virtual const char* name() { return "DrawRects"; }
 
+    virtual DeferredDisplayList::OpBatchId getBatchId() {
+        return DeferredDisplayList::kOpBatch_Vertices;
+    }
+
 private:
     const float* mRects;
     int mCount;
@@ -910,22 +1012,24 @@
 
     virtual const char* name() { return "DrawPath"; }
 
+    virtual DeferredDisplayList::OpBatchId getBatchId() {
+        return DeferredDisplayList::kOpBatch_AlphaMaskTexture;
+    }
 private:
     SkPath* mPath;
 };
 
-class DrawLinesOp : public DrawOp {
+class DrawLinesOp : public DrawBoundedOp {
 public:
     DrawLinesOp(float* points, int count, SkPaint* paint)
-            : DrawOp(paint), mPoints(points), mCount(count) {
-        /* TODO: inherit from DrawBoundedOp and calculate localbounds something like:
+            : DrawBoundedOp(paint), mPoints(points), mCount(count) {
         for (int i = 0; i < count; i += 2) {
             mLocalBounds.left = fminf(mLocalBounds.left, points[i]);
             mLocalBounds.right = fmaxf(mLocalBounds.right, points[i]);
             mLocalBounds.top = fminf(mLocalBounds.top, points[i+1]);
             mLocalBounds.bottom = fmaxf(mLocalBounds.bottom, points[i+1]);
         }
-        */
+        mLocalBounds.outset(strokeWidthOutset());
     }
 
     virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
@@ -939,6 +1043,12 @@
 
     virtual const char* name() { return "DrawLines"; }
 
+    virtual DeferredDisplayList::OpBatchId getBatchId() {
+        return mPaint->isAntiAlias() ?
+                DeferredDisplayList::kOpBatch_AlphaVertices :
+                DeferredDisplayList::kOpBatch_Vertices;
+    }
+
 protected:
     float* mPoints;
     int mCount;
@@ -969,6 +1079,12 @@
     virtual void output(int level, uint32_t flags) {
         OP_LOG("Draw some text, %d bytes", mBytesCount);
     }
+
+    virtual DeferredDisplayList::OpBatchId getBatchId() {
+        return mPaint->getColor() == 0xff000000 ?
+                DeferredDisplayList::kOpBatch_Text :
+                DeferredDisplayList::kOpBatch_ColorText;
+    }
 protected:
     const char* mText;
     int mBytesCount;
@@ -1040,6 +1156,12 @@
 
     virtual const char* name() { return "DrawText"; }
 
+    virtual DeferredDisplayList::OpBatchId getBatchId() {
+        return mPaint->getColor() == 0xff000000 ?
+                DeferredDisplayList::kOpBatch_Text :
+                DeferredDisplayList::kOpBatch_ColorText;
+    }
+
 private:
     const char* mText;
     int mBytesCount;
@@ -1081,9 +1203,21 @@
 public:
     DrawDisplayListOp(DisplayList* displayList, int flags)
             : DrawOp(0), mDisplayList(displayList), mFlags(flags) {}
+
+    virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, int saveCount,
+            uint32_t level, bool caching, int multipliedAlpha, DeferredDisplayList& deferredList) {
+        if (mDisplayList && mDisplayList->isRenderable()) {
+            return mDisplayList->replay(renderer, dirty, mFlags, level + 1, &deferredList);
+        }
+        return DrawGlInfo::kStatusDone;
+    }
+
     virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
             bool caching, int multipliedAlpha) {
-        return renderer.drawDisplayList(mDisplayList, dirty, mFlags, level + 1);
+        if (mDisplayList && mDisplayList->isRenderable()) {
+            return mDisplayList->replay(renderer, dirty, mFlags, level + 1);
+        }
+        return DrawGlInfo::kStatusDone;
     }
 
     virtual void output(int level, uint32_t flags) {
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 17ed8ac..710f12f 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -20,6 +20,8 @@
 
 #include <private/hwui/DrawGlInfo.h>
 
+#include "DisplayList.h"
+#include "DeferredDisplayList.h"
 #include "DisplayListLogBuffer.h"
 #include "DisplayListOp.h"
 #include "DisplayListRenderer.h"
@@ -28,425 +30,6 @@
 namespace android {
 namespace uirenderer {
 
-///////////////////////////////////////////////////////////////////////////////
-// Display list
-///////////////////////////////////////////////////////////////////////////////
-
-void DisplayList::outputLogBuffer(int fd) {
-    DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
-    if (logBuffer.isEmpty()) {
-        return;
-    }
-
-    FILE *file = fdopen(fd, "a");
-
-    fprintf(file, "\nRecent DisplayList operations\n");
-    logBuffer.outputCommands(file);
-
-    String8 cachesLog;
-    Caches::getInstance().dumpMemoryUsage(cachesLog);
-    fprintf(file, "\nCaches:\n%s", cachesLog.string());
-    fprintf(file, "\n");
-
-    fflush(file);
-}
-
-DisplayList::DisplayList(const DisplayListRenderer& recorder) :
-    mTransformMatrix(NULL), mTransformCamera(NULL), mTransformMatrix3D(NULL),
-    mStaticMatrix(NULL), mAnimationMatrix(NULL) {
-
-    initFromDisplayListRenderer(recorder);
-}
-
-DisplayList::~DisplayList() {
-    clearResources();
-}
-
-void DisplayList::destroyDisplayListDeferred(DisplayList* displayList) {
-    if (displayList) {
-        DISPLAY_LIST_LOGD("Deferring display list destruction");
-        Caches::getInstance().deleteDisplayListDeferred(displayList);
-    }
-}
-
-void DisplayList::clearResources() {
-    mDisplayListData = NULL;
-    delete mTransformMatrix;
-    delete mTransformCamera;
-    delete mTransformMatrix3D;
-    delete mStaticMatrix;
-    delete mAnimationMatrix;
-
-    mTransformMatrix = NULL;
-    mTransformCamera = NULL;
-    mTransformMatrix3D = NULL;
-    mStaticMatrix = NULL;
-    mAnimationMatrix = NULL;
-
-    Caches& caches = Caches::getInstance();
-    caches.unregisterFunctors(mFunctorCount);
-    caches.resourceCache.lock();
-
-    for (size_t i = 0; i < mBitmapResources.size(); i++) {
-        caches.resourceCache.decrementRefcountLocked(mBitmapResources.itemAt(i));
-    }
-
-    for (size_t i = 0; i < mOwnedBitmapResources.size(); i++) {
-        SkBitmap* bitmap = mOwnedBitmapResources.itemAt(i);
-        caches.resourceCache.decrementRefcountLocked(bitmap);
-        caches.resourceCache.destructorLocked(bitmap);
-    }
-
-    for (size_t i = 0; i < mFilterResources.size(); i++) {
-        caches.resourceCache.decrementRefcountLocked(mFilterResources.itemAt(i));
-    }
-
-    for (size_t i = 0; i < mShaders.size(); i++) {
-        caches.resourceCache.decrementRefcountLocked(mShaders.itemAt(i));
-        caches.resourceCache.destructorLocked(mShaders.itemAt(i));
-    }
-
-    for (size_t i = 0; i < mSourcePaths.size(); i++) {
-        caches.resourceCache.decrementRefcountLocked(mSourcePaths.itemAt(i));
-    }
-
-    for (size_t i = 0; i < mLayers.size(); i++) {
-        caches.resourceCache.decrementRefcountLocked(mLayers.itemAt(i));
-    }
-
-    caches.resourceCache.unlock();
-
-    for (size_t i = 0; i < mPaints.size(); i++) {
-        delete mPaints.itemAt(i);
-    }
-
-    for (size_t i = 0; i < mRegions.size(); i++) {
-        delete mRegions.itemAt(i);
-    }
-
-    for (size_t i = 0; i < mPaths.size(); i++) {
-        SkPath* path = mPaths.itemAt(i);
-        caches.pathCache.remove(path);
-        delete path;
-    }
-
-    for (size_t i = 0; i < mMatrices.size(); i++) {
-        delete mMatrices.itemAt(i);
-    }
-
-    mBitmapResources.clear();
-    mOwnedBitmapResources.clear();
-    mFilterResources.clear();
-    mShaders.clear();
-    mSourcePaths.clear();
-    mPaints.clear();
-    mRegions.clear();
-    mPaths.clear();
-    mMatrices.clear();
-    mLayers.clear();
-}
-
-void DisplayList::reset() {
-    clearResources();
-    init();
-}
-
-void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorder, bool reusing) {
-    if (reusing) {
-        // re-using display list - clear out previous allocations
-        clearResources();
-    }
-
-    init();
-
-    mDisplayListData = recorder.getDisplayListData();
-    mSize = mDisplayListData->allocator.usedSize();
-
-    if (mSize == 0) {
-        return;
-    }
-
-    mFunctorCount = recorder.getFunctorCount();
-
-    Caches& caches = Caches::getInstance();
-    caches.registerFunctors(mFunctorCount);
-    caches.resourceCache.lock();
-
-    const Vector<SkBitmap*>& bitmapResources = recorder.getBitmapResources();
-    for (size_t i = 0; i < bitmapResources.size(); i++) {
-        SkBitmap* resource = bitmapResources.itemAt(i);
-        mBitmapResources.add(resource);
-        caches.resourceCache.incrementRefcountLocked(resource);
-    }
-
-    const Vector<SkBitmap*> &ownedBitmapResources = recorder.getOwnedBitmapResources();
-    for (size_t i = 0; i < ownedBitmapResources.size(); i++) {
-        SkBitmap* resource = ownedBitmapResources.itemAt(i);
-        mOwnedBitmapResources.add(resource);
-        caches.resourceCache.incrementRefcountLocked(resource);
-    }
-
-    const Vector<SkiaColorFilter*>& filterResources = recorder.getFilterResources();
-    for (size_t i = 0; i < filterResources.size(); i++) {
-        SkiaColorFilter* resource = filterResources.itemAt(i);
-        mFilterResources.add(resource);
-        caches.resourceCache.incrementRefcountLocked(resource);
-    }
-
-    const Vector<SkiaShader*>& shaders = recorder.getShaders();
-    for (size_t i = 0; i < shaders.size(); i++) {
-        SkiaShader* resource = shaders.itemAt(i);
-        mShaders.add(resource);
-        caches.resourceCache.incrementRefcountLocked(resource);
-    }
-
-    const SortedVector<SkPath*>& sourcePaths = recorder.getSourcePaths();
-    for (size_t i = 0; i < sourcePaths.size(); i++) {
-        mSourcePaths.add(sourcePaths.itemAt(i));
-        caches.resourceCache.incrementRefcountLocked(sourcePaths.itemAt(i));
-    }
-
-    const Vector<Layer*>& layers = recorder.getLayers();
-    for (size_t i = 0; i < layers.size(); i++) {
-        mLayers.add(layers.itemAt(i));
-        caches.resourceCache.incrementRefcountLocked(layers.itemAt(i));
-    }
-
-    caches.resourceCache.unlock();
-
-    mPaints.appendVector(recorder.getPaints());
-    mRegions.appendVector(recorder.getRegions());
-    mPaths.appendVector(recorder.getPaths());
-    mMatrices.appendVector(recorder.getMatrices());
-}
-
-void DisplayList::init() {
-    mSize = 0;
-    mIsRenderable = true;
-    mFunctorCount = 0;
-    mLeft = 0;
-    mTop = 0;
-    mRight = 0;
-    mBottom = 0;
-    mClipChildren = true;
-    mAlpha = 1;
-    mMultipliedAlpha = 255;
-    mHasOverlappingRendering = true;
-    mTranslationX = 0;
-    mTranslationY = 0;
-    mRotation = 0;
-    mRotationX = 0;
-    mRotationY= 0;
-    mScaleX = 1;
-    mScaleY = 1;
-    mPivotX = 0;
-    mPivotY = 0;
-    mCameraDistance = 0;
-    mMatrixDirty = false;
-    mMatrixFlags = 0;
-    mPrevWidth = -1;
-    mPrevHeight = -1;
-    mWidth = 0;
-    mHeight = 0;
-    mPivotExplicitlySet = false;
-    mCaching = false;
-}
-
-size_t DisplayList::getSize() {
-    return mSize;
-}
-
-/**
- * This function is a simplified version of replay(), where we simply retrieve and log the
- * display list. This function should remain in sync with the replay() function.
- */
-void DisplayList::output(uint32_t level) {
-    ALOGD("%*sStart display list (%p, %s, render=%d)", level * 2, "", this,
-            mName.string(), isRenderable());
-
-    ALOGD("%*s%s %d", level * 2, "", "Save", SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
-    outputViewProperties(level);
-    int flags = DisplayListOp::kOpLogFlag_Recurse;
-    for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
-        mDisplayListData->displayListOps[i]->output(level, flags);
-    }
-    ALOGD("%*sDone (%p, %s)", level * 2, "", this, mName.string());
-}
-
-void DisplayList::updateMatrix() {
-    if (mMatrixDirty) {
-        if (!mTransformMatrix) {
-            mTransformMatrix = new SkMatrix();
-        }
-        if (mMatrixFlags == 0 || mMatrixFlags == TRANSLATION) {
-            mTransformMatrix->reset();
-        } else {
-            if (!mPivotExplicitlySet) {
-                if (mWidth != mPrevWidth || mHeight != mPrevHeight) {
-                    mPrevWidth = mWidth;
-                    mPrevHeight = mHeight;
-                    mPivotX = mPrevWidth / 2;
-                    mPivotY = mPrevHeight / 2;
-                }
-            }
-            if ((mMatrixFlags & ROTATION_3D) == 0) {
-                mTransformMatrix->setTranslate(mTranslationX, mTranslationY);
-                mTransformMatrix->preRotate(mRotation, mPivotX, mPivotY);
-                mTransformMatrix->preScale(mScaleX, mScaleY, mPivotX, mPivotY);
-            } else {
-                if (!mTransformCamera) {
-                    mTransformCamera = new Sk3DView();
-                    mTransformMatrix3D = new SkMatrix();
-                }
-                mTransformMatrix->reset();
-                mTransformCamera->save();
-                mTransformMatrix->preScale(mScaleX, mScaleY, mPivotX, mPivotY);
-                mTransformCamera->rotateX(mRotationX);
-                mTransformCamera->rotateY(mRotationY);
-                mTransformCamera->rotateZ(-mRotation);
-                mTransformCamera->getMatrix(mTransformMatrix3D);
-                mTransformMatrix3D->preTranslate(-mPivotX, -mPivotY);
-                mTransformMatrix3D->postTranslate(mPivotX + mTranslationX,
-                        mPivotY + mTranslationY);
-                mTransformMatrix->postConcat(*mTransformMatrix3D);
-                mTransformCamera->restore();
-            }
-        }
-        mMatrixDirty = false;
-    }
-}
-
-void DisplayList::outputViewProperties(uint32_t level) {
-    updateMatrix();
-    if (mLeft != 0 || mTop != 0) {
-        ALOGD("%*sTranslate (left, top) %d, %d", level * 2, "", mLeft, mTop);
-    }
-    if (mStaticMatrix) {
-        ALOGD("%*sConcatMatrix (static) %p: " MATRIX_STRING,
-                level * 2, "", mStaticMatrix, MATRIX_ARGS(mStaticMatrix));
-    }
-    if (mAnimationMatrix) {
-        ALOGD("%*sConcatMatrix (animation) %p: " MATRIX_STRING,
-                level * 2, "", mAnimationMatrix, MATRIX_ARGS(mStaticMatrix));
-    }
-    if (mMatrixFlags != 0) {
-        if (mMatrixFlags == TRANSLATION) {
-            ALOGD("%*sTranslate %f, %f", level * 2, "", mTranslationX, mTranslationY);
-        } else {
-            ALOGD("%*sConcatMatrix %p: " MATRIX_STRING,
-                    level * 2, "", mTransformMatrix, MATRIX_ARGS(mTransformMatrix));
-        }
-    }
-    if (mAlpha < 1 && !mCaching) {
-        if (!mHasOverlappingRendering) {
-            ALOGD("%*sSetAlpha %.2f", level * 2, "", mAlpha);
-        } else {
-            int flags = SkCanvas::kHasAlphaLayer_SaveFlag;
-            if (mClipChildren) {
-                flags |= SkCanvas::kClipToLayer_SaveFlag;
-            }
-            ALOGD("%*sSaveLayerAlpha %.2f, %.2f, %.2f, %.2f, %d, 0x%x", level * 2, "",
-                    (float) 0, (float) 0, (float) mRight - mLeft, (float) mBottom - mTop,
-                    mMultipliedAlpha, flags);
-        }
-    }
-    if (mClipChildren && !mCaching) {
-        ALOGD("%*sClipRect %.2f, %.2f, %.2f, %.2f", level * 2, "", 0.0f, 0.0f,
-                (float) mRight - mLeft, (float) mBottom - mTop);
-    }
-}
-
-void DisplayList::setViewProperties(OpenGLRenderer& renderer, uint32_t level) {
-#if DEBUG_DISPLAYLIST
-    outputViewProperties(level);
-#endif
-    updateMatrix();
-    if (mLeft != 0 || mTop != 0) {
-        renderer.translate(mLeft, mTop);
-    }
-    if (mStaticMatrix) {
-        renderer.concatMatrix(mStaticMatrix);
-    } else if (mAnimationMatrix) {
-        renderer.concatMatrix(mAnimationMatrix);
-    }
-    if (mMatrixFlags != 0) {
-        if (mMatrixFlags == TRANSLATION) {
-            renderer.translate(mTranslationX, mTranslationY);
-        } else {
-            renderer.concatMatrix(mTransformMatrix);
-        }
-    }
-    if (mAlpha < 1 && !mCaching) {
-        if (!mHasOverlappingRendering) {
-            renderer.setAlpha(mAlpha);
-        } else {
-            // TODO: should be able to store the size of a DL at record time and not
-            // have to pass it into this call. In fact, this information might be in the
-            // location/size info that we store with the new native transform data.
-            int flags = SkCanvas::kHasAlphaLayer_SaveFlag;
-            if (mClipChildren) {
-                flags |= SkCanvas::kClipToLayer_SaveFlag;
-            }
-            renderer.saveLayerAlpha(0, 0, mRight - mLeft, mBottom - mTop,
-                    mMultipliedAlpha, flags);
-        }
-    }
-    if (mClipChildren && !mCaching) {
-        renderer.clipRect(0, 0, mRight - mLeft, mBottom - mTop,
-                SkRegion::kIntersect_Op);
-    }
-}
-
-status_t DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, uint32_t level) {
-    status_t drawGlStatus = DrawGlInfo::kStatusDone;
-
-#if DEBUG_DISPLAY_LIST
-    Rect* clipRect = renderer.getClipRect();
-    DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), clipRect: %.0f, %.f, %.0f, %.0f",
-            (level+1)*2, "", this, mName.string(), clipRect->left, clipRect->top,
-            clipRect->right, clipRect->bottom);
-#endif
-
-    renderer.startMark(mName.string());
-
-    int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
-    DISPLAY_LIST_LOGD("%*sSave %d %d", level * 2, "",
-            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);
-    setViewProperties(renderer, level);
-
-    if (renderer.quickRejectNoScissor(0, 0, mWidth, mHeight)) {
-        DISPLAY_LIST_LOGD("%*sRestoreToCount %d", level * 2, "", restoreTo);
-        renderer.restoreToCount(restoreTo);
-        renderer.endMark();
-        return drawGlStatus;
-    }
-
-    DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
-    int saveCount = renderer.getSaveCount() - 1;
-    for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
-        DisplayListOp *op = mDisplayListData->displayListOps[i];
-#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
-        Caches::getInstance().eventMark(strlen(op->name()), op->name());
-#endif
-
-        drawGlStatus |= op->replay(renderer, dirty, flags,
-                saveCount, level, mCaching, mMultipliedAlpha);
-        logBuffer.writeCommand(level, op->name());
-    }
-
-    DISPLAY_LIST_LOGD("%*sRestoreToCount %d", level * 2, "", restoreTo);
-    renderer.restoreToCount(restoreTo);
-    renderer.endMark();
-
-    DISPLAY_LIST_LOGD("%*sDone (%p, %s), returning %d", (level + 1) * 2, "", this, mName.string(),
-            drawGlStatus);
-    return drawGlStatus;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Base structure
-///////////////////////////////////////////////////////////////////////////////
-
 DisplayListRenderer::DisplayListRenderer():
         mCaches(Caches::getInstance()), mDisplayListData(new DisplayListData),
         mTranslateX(0.0f), mTranslateY(0.0f), mHasTranslate(false),
@@ -657,7 +240,7 @@
 }
 
 status_t DisplayListRenderer::drawDisplayList(DisplayList* displayList,
-        Rect& dirty, int32_t flags, uint32_t level) {
+        Rect& dirty, int32_t flags) {
     // dirty is an out parameter and should not be recorded,
     // it matters only when replaying the display list
 
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index b25288b..bff3b97 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -17,21 +17,14 @@
 #ifndef ANDROID_HWUI_DISPLAY_LIST_RENDERER_H
 #define ANDROID_HWUI_DISPLAY_LIST_RENDERER_H
 
-#include <SkChunkAlloc.h>
-#include <SkFlattenable.h>
 #include <SkMatrix.h>
-#include <SkCamera.h>
 #include <SkPaint.h>
 #include <SkPath.h>
-#include <SkRefCnt.h>
-#include <SkTDArray.h>
-#include <SkTSearch.h>
-
 #include <cutils/compiler.h>
 
+#include "DisplayList.h"
 #include "DisplayListLogBuffer.h"
 #include "OpenGLRenderer.h"
-#include "utils/LinearAllocator.h"
 
 namespace android {
 namespace uirenderer {
@@ -50,390 +43,17 @@
     #define DISPLAY_LIST_LOGD(...)
 #endif
 
-#define TRANSLATION 0x0001
-#define ROTATION    0x0002
-#define ROTATION_3D 0x0004
-#define SCALE       0x0008
-#define PIVOT       0x0010
-
 ///////////////////////////////////////////////////////////////////////////////
 // Display list
 ///////////////////////////////////////////////////////////////////////////////
 
+class DeferredDisplayList;
 class DisplayListRenderer;
 class DisplayListOp;
 class DrawOp;
 class StateOp;
 
 /**
- * Refcounted structure that holds data used in display list stream
- */
-class DisplayListData: public LightRefBase<DisplayListData> {
-public:
-    LinearAllocator allocator;
-    Vector<DisplayListOp*> displayListOps;
-};
-
-/**
- * Replays recorded drawing commands.
- */
-class DisplayList {
-public:
-    DisplayList(const DisplayListRenderer& recorder);
-    ANDROID_API ~DisplayList();
-
-    // See flags defined in DisplayList.java
-    enum ReplayFlag {
-        kReplayFlag_ClipChildren = 0x1
-    };
-
-    void setViewProperties(OpenGLRenderer& renderer, uint32_t level);
-    void outputViewProperties(uint32_t level);
-
-    ANDROID_API size_t getSize();
-    ANDROID_API static void destroyDisplayListDeferred(DisplayList* displayList);
-    ANDROID_API static void outputLogBuffer(int fd);
-
-    void initFromDisplayListRenderer(const DisplayListRenderer& recorder, bool reusing = false);
-
-    status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, uint32_t level = 0);
-
-    void output(uint32_t level = 0);
-
-    ANDROID_API void reset();
-
-    void setRenderable(bool renderable) {
-        mIsRenderable = renderable;
-    }
-
-    bool isRenderable() const {
-        return mIsRenderable;
-    }
-
-    void setName(const char* name) {
-        if (name) {
-            mName.setTo(name);
-        }
-    }
-
-    void setClipChildren(bool clipChildren) {
-        mClipChildren = clipChildren;
-    }
-
-    void setStaticMatrix(SkMatrix* matrix) {
-        delete mStaticMatrix;
-        mStaticMatrix = new SkMatrix(*matrix);
-    }
-
-    void setAnimationMatrix(SkMatrix* matrix) {
-        delete mAnimationMatrix;
-        if (matrix) {
-            mAnimationMatrix = new SkMatrix(*matrix);
-        } else {
-            mAnimationMatrix = NULL;
-        }
-    }
-
-    void setAlpha(float alpha) {
-        alpha = fminf(1.0f, fmaxf(0.0f, alpha));
-        if (alpha != mAlpha) {
-            mAlpha = alpha;
-            mMultipliedAlpha = (int) (255 * alpha);
-        }
-    }
-
-    void setHasOverlappingRendering(bool hasOverlappingRendering) {
-        mHasOverlappingRendering = hasOverlappingRendering;
-    }
-
-    void setTranslationX(float translationX) {
-        if (translationX != mTranslationX) {
-            mTranslationX = translationX;
-            mMatrixDirty = true;
-            if (mTranslationX == 0.0f && mTranslationY == 0.0f) {
-                mMatrixFlags &= ~TRANSLATION;
-            } else {
-                mMatrixFlags |= TRANSLATION;
-            }
-        }
-    }
-
-    void setTranslationY(float translationY) {
-        if (translationY != mTranslationY) {
-            mTranslationY = translationY;
-            mMatrixDirty = true;
-            if (mTranslationX == 0.0f && mTranslationY == 0.0f) {
-                mMatrixFlags &= ~TRANSLATION;
-            } else {
-                mMatrixFlags |= TRANSLATION;
-            }
-        }
-    }
-
-    void setRotation(float rotation) {
-        if (rotation != mRotation) {
-            mRotation = rotation;
-            mMatrixDirty = true;
-            if (mRotation == 0.0f) {
-                mMatrixFlags &= ~ROTATION;
-            } else {
-                mMatrixFlags |= ROTATION;
-            }
-        }
-    }
-
-    void setRotationX(float rotationX) {
-        if (rotationX != mRotationX) {
-            mRotationX = rotationX;
-            mMatrixDirty = true;
-            if (mRotationX == 0.0f && mRotationY == 0.0f) {
-                mMatrixFlags &= ~ROTATION_3D;
-            } else {
-                mMatrixFlags |= ROTATION_3D;
-            }
-        }
-    }
-
-    void setRotationY(float rotationY) {
-        if (rotationY != mRotationY) {
-            mRotationY = rotationY;
-            mMatrixDirty = true;
-            if (mRotationX == 0.0f && mRotationY == 0.0f) {
-                mMatrixFlags &= ~ROTATION_3D;
-            } else {
-                mMatrixFlags |= ROTATION_3D;
-            }
-        }
-    }
-
-    void setScaleX(float scaleX) {
-        if (scaleX != mScaleX) {
-            mScaleX = scaleX;
-            mMatrixDirty = true;
-            if (mScaleX == 1.0f && mScaleY == 1.0f) {
-                mMatrixFlags &= ~SCALE;
-            } else {
-                mMatrixFlags |= SCALE;
-            }
-        }
-    }
-
-    void setScaleY(float scaleY) {
-        if (scaleY != mScaleY) {
-            mScaleY = scaleY;
-            mMatrixDirty = true;
-            if (mScaleX == 1.0f && mScaleY == 1.0f) {
-                mMatrixFlags &= ~SCALE;
-            } else {
-                mMatrixFlags |= SCALE;
-            }
-        }
-    }
-
-    void setPivotX(float pivotX) {
-        mPivotX = pivotX;
-        mMatrixDirty = true;
-        if (mPivotX == 0.0f && mPivotY == 0.0f) {
-            mMatrixFlags &= ~PIVOT;
-        } else {
-            mMatrixFlags |= PIVOT;
-        }
-        mPivotExplicitlySet = true;
-    }
-
-    void setPivotY(float pivotY) {
-        mPivotY = pivotY;
-        mMatrixDirty = true;
-        if (mPivotX == 0.0f && mPivotY == 0.0f) {
-            mMatrixFlags &= ~PIVOT;
-        } else {
-            mMatrixFlags |= PIVOT;
-        }
-        mPivotExplicitlySet = true;
-    }
-
-    void setCameraDistance(float distance) {
-        if (distance != mCameraDistance) {
-            mCameraDistance = distance;
-            mMatrixDirty = true;
-            if (!mTransformCamera) {
-                mTransformCamera = new Sk3DView();
-                mTransformMatrix3D = new SkMatrix();
-            }
-            mTransformCamera->setCameraLocation(0, 0, distance);
-        }
-    }
-
-    void setLeft(int left) {
-        if (left != mLeft) {
-            mLeft = left;
-            mWidth = mRight - mLeft;
-            if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
-                mMatrixDirty = true;
-            }
-        }
-    }
-
-    void setTop(int top) {
-        if (top != mTop) {
-            mTop = top;
-            mHeight = mBottom - mTop;
-            if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
-                mMatrixDirty = true;
-            }
-        }
-    }
-
-    void setRight(int right) {
-        if (right != mRight) {
-            mRight = right;
-            mWidth = mRight - mLeft;
-            if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
-                mMatrixDirty = true;
-            }
-        }
-    }
-
-    void setBottom(int bottom) {
-        if (bottom != mBottom) {
-            mBottom = bottom;
-            mHeight = mBottom - mTop;
-            if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
-                mMatrixDirty = true;
-            }
-        }
-    }
-
-    void setLeftTop(int left, int top) {
-        if (left != mLeft || top != mTop) {
-            mLeft = left;
-            mTop = top;
-            mWidth = mRight - mLeft;
-            mHeight = mBottom - mTop;
-            if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
-                mMatrixDirty = true;
-            }
-        }
-    }
-
-    void setLeftTopRightBottom(int left, int top, int right, int bottom) {
-        if (left != mLeft || top != mTop || right != mRight || bottom != mBottom) {
-            mLeft = left;
-            mTop = top;
-            mRight = right;
-            mBottom = bottom;
-            mWidth = mRight - mLeft;
-            mHeight = mBottom - mTop;
-            if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
-                mMatrixDirty = true;
-            }
-        }
-    }
-
-    void offsetLeftRight(int offset) {
-        if (offset != 0) {
-            mLeft += offset;
-            mRight += offset;
-            if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
-                mMatrixDirty = true;
-            }
-        }
-    }
-
-    void offsetTopBottom(int offset) {
-        if (offset != 0) {
-            mTop += offset;
-            mBottom += offset;
-            if (mMatrixFlags > TRANSLATION && !mPivotExplicitlySet) {
-                mMatrixDirty = true;
-            }
-        }
-    }
-
-    void setCaching(bool caching) {
-        mCaching = caching;
-    }
-
-    int getWidth() {
-        return mWidth;
-    }
-
-    int getHeight() {
-        return mHeight;
-    }
-
-private:
-    void init();
-
-    void clearResources();
-
-    void updateMatrix();
-
-    class TextContainer {
-    public:
-        size_t length() const {
-            return mByteLength;
-        }
-
-        const char* text() const {
-            return (const char*) mText;
-        }
-
-        size_t mByteLength;
-        const char* mText;
-    };
-
-    Vector<SkBitmap*> mBitmapResources;
-    Vector<SkBitmap*> mOwnedBitmapResources;
-    Vector<SkiaColorFilter*> mFilterResources;
-
-    Vector<SkPaint*> mPaints;
-    Vector<SkPath*> mPaths;
-    SortedVector<SkPath*> mSourcePaths;
-    Vector<SkRegion*> mRegions;
-    Vector<SkMatrix*> mMatrices;
-    Vector<SkiaShader*> mShaders;
-    Vector<Layer*> mLayers;
-
-    sp<DisplayListData> mDisplayListData;
-
-    size_t mSize;
-
-    bool mIsRenderable;
-    uint32_t mFunctorCount;
-
-    String8 mName;
-
-    // View properties
-    bool mClipChildren;
-    float mAlpha;
-    int mMultipliedAlpha;
-    bool mHasOverlappingRendering;
-    float mTranslationX, mTranslationY;
-    float mRotation, mRotationX, mRotationY;
-    float mScaleX, mScaleY;
-    float mPivotX, mPivotY;
-    float mCameraDistance;
-    int mLeft, mTop, mRight, mBottom;
-    int mWidth, mHeight;
-    int mPrevWidth, mPrevHeight;
-    bool mPivotExplicitlySet;
-    bool mMatrixDirty;
-    bool mMatrixIsIdentity;
-    uint32_t mMatrixFlags;
-    SkMatrix* mTransformMatrix;
-    Sk3DView* mTransformCamera;
-    SkMatrix* mTransformMatrix3D;
-    SkMatrix* mStaticMatrix;
-    SkMatrix* mAnimationMatrix;
-    bool mCaching;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-// Renderer
-///////////////////////////////////////////////////////////////////////////////
-
-/**
  * Records drawing commands in a display list for latter playback.
  */
 class DisplayListRenderer: public OpenGLRenderer {
@@ -475,8 +95,7 @@
     virtual bool clipPath(SkPath* path, SkRegion::Op op);
     virtual bool clipRegion(SkRegion* region, SkRegion::Op op);
 
-    virtual status_t drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t flags,
-            uint32_t level = 0);
+    virtual status_t drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t flags);
     virtual status_t drawLayer(Layer* layer, float x, float y, SkPaint* paint);
     virtual status_t drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
     virtual status_t drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint);
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index 79fae2b..5cec5a8 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -24,7 +24,6 @@
 
 #include <SkMatrix.h>
 
-#include "utils/Compare.h"
 #include "Matrix.h"
 
 namespace android {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 57d16ae..d11558f 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -32,6 +32,7 @@
 #include <ui/Rect.h>
 
 #include "OpenGLRenderer.h"
+#include "DeferredDisplayList.h"
 #include "DisplayListRenderer.h"
 #include "PathTessellator.h"
 #include "Properties.h"
@@ -111,16 +112,18 @@
 
 OpenGLRenderer::OpenGLRenderer():
         mCaches(Caches::getInstance()), mExtensions(Extensions::getInstance()) {
-    mShader = NULL;
-    mColorFilter = NULL;
-    mHasShadow = false;
-    mHasDrawFilter = false;
+    mDrawModifiers.mShader = NULL;
+    mDrawModifiers.mColorFilter = NULL;
+    mDrawModifiers.mHasShadow = false;
+    mDrawModifiers.mHasDrawFilter = false;
 
     memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices));
 
     mFirstSnapshot = new Snapshot;
 
     mScissorOptimizationDisabled = false;
+    mDrawDeferDisabled = false;
+    mDrawReorderDisabled = false;
 }
 
 OpenGLRenderer::~OpenGLRenderer() {
@@ -137,6 +140,20 @@
     } else {
         INIT_LOGD("  Scissor optimization enabled");
     }
+
+    if (property_get(PROPERTY_DISABLE_DRAW_DEFER, property, "false")) {
+        mDrawDeferDisabled = !strcasecmp(property, "true");
+        INIT_LOGD("  Draw defer %s", mDrawDeferDisabled ? "disabled" : "enabled");
+    } else {
+        INIT_LOGD("  Draw defer enabled");
+    }
+
+    if (property_get(PROPERTY_DISABLE_DRAW_REORDER, property, "false")) {
+        mDrawReorderDisabled = !strcasecmp(property, "true");
+        INIT_LOGD("  Draw reorder %s", mDrawReorderDisabled ? "disabled" : "enabled");
+    } else {
+        INIT_LOGD("  Draw reorder enabled");
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -770,7 +787,7 @@
     layer->layer.set(bounds);
     layer->texCoords.set(0.0f, bounds.getHeight() / float(layer->getHeight()),
             bounds.getWidth() / float(layer->getWidth()), 0.0f);
-    layer->setColorFilter(mColorFilter);
+    layer->setColorFilter(mDrawModifiers.mColorFilter);
     layer->setBlend(true);
     layer->setDirty(false);
 
@@ -1204,6 +1221,40 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
+// State Deferral
+///////////////////////////////////////////////////////////////////////////////
+
+bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state) {
+    const Rect& currentClip = *(mSnapshot->clipRect);
+    const mat4& currentMatrix = *(mSnapshot->transform);
+
+    // state only has bounds initialized in local coordinates
+    if (!state.mBounds.isEmpty()) {
+        currentMatrix.mapRect(state.mBounds);
+        if (!state.mBounds.intersect(currentClip)) {
+            // quick rejected
+            return true;
+        }
+    }
+
+    state.mClip.set(currentClip);
+    state.mMatrix.load(currentMatrix);
+    state.mDrawModifiers = mDrawModifiers;
+    return false;
+}
+
+void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state) {
+    mSnapshot->transform->load(state.mMatrix);
+
+    // NOTE: a clip RECT will be saved and restored, but DeferredDisplayState doesn't support
+    // complex clips. In the future, we should add support for deferral of operations clipped by
+    // these. for now, we don't defer with complex clips (see OpenGLRenderer::disallowDeferral())
+    mSnapshot->setClip(state.mClip.left, state.mClip.top, state.mClip.right, state.mClip.bottom);
+    dirtyClip();
+    mDrawModifiers = state.mDrawModifiers;
+}
+
+///////////////////////////////////////////////////////////////////////////////
 // Transforms
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -1452,7 +1503,7 @@
     if (clear) clearLayerRegions();
     // Make sure setScissor & setStencil happen at the beginning of
     // this method
-    if (mDirtyClip) {
+    if (mDirtyClip && mCaches.scissorEnabled) {
         setScissorFromClip();
         setStencilFromClip();
     }
@@ -1524,14 +1575,14 @@
 }
 
 void OpenGLRenderer::setupDrawShader() {
-    if (mShader) {
-        mShader->describe(mDescription, mExtensions);
+    if (mDrawModifiers.mShader) {
+        mDrawModifiers.mShader->describe(mDescription, mExtensions);
     }
 }
 
 void OpenGLRenderer::setupDrawColorFilter() {
-    if (mColorFilter) {
-        mColorFilter->describe(mDescription, mExtensions);
+    if (mDrawModifiers.mColorFilter) {
+        mDrawModifiers.mColorFilter->describe(mDescription, mExtensions);
     }
 }
 
@@ -1547,16 +1598,19 @@
     // When the blending mode is kClear_Mode, we need to use a modulate color
     // argb=1,0,0,0
     accountForClear(mode);
-    chooseBlending((mColorSet && mColorA < 1.0f) || (mShader && mShader->blend()), mode,
-            mDescription, swapSrcDst);
+    bool blend = (mColorSet && mColorA < 1.0f) ||
+            (mDrawModifiers.mShader && mDrawModifiers.mShader->blend());
+    chooseBlending(blend, mode, mDescription, swapSrcDst);
 }
 
 void OpenGLRenderer::setupDrawBlending(bool blend, SkXfermode::Mode mode, bool swapSrcDst) {
     // When the blending mode is kClear_Mode, we need to use a modulate color
     // argb=1,0,0,0
     accountForClear(mode);
-    chooseBlending(blend || (mColorSet && mColorA < 1.0f) || (mShader && mShader->blend()) ||
-            (mColorFilter && mColorFilter->blend()), mode, mDescription, swapSrcDst);
+    blend |= (mColorSet && mColorA < 1.0f) ||
+            (mDrawModifiers.mShader && mDrawModifiers.mShader->blend()) ||
+            (mDrawModifiers.mColorFilter && mDrawModifiers.mColorFilter->blend());
+    chooseBlending(blend, mode, mDescription, swapSrcDst);
 }
 
 void OpenGLRenderer::setupDrawProgram() {
@@ -1609,7 +1663,7 @@
 }
 
 void OpenGLRenderer::setupDrawColorUniforms() {
-    if ((mColorSet && !mShader) || (mShader && mSetShaderColor)) {
+    if ((mColorSet && !mDrawModifiers.mShader) || (mDrawModifiers.mShader && mSetShaderColor)) {
         mCaches.currentProgram->setColor(mColorR, mColorG, mColorB, mColorA);
     }
 }
@@ -1621,23 +1675,25 @@
 }
 
 void OpenGLRenderer::setupDrawShaderUniforms(bool ignoreTransform) {
-    if (mShader) {
+    if (mDrawModifiers.mShader) {
         if (ignoreTransform) {
             mModelView.loadInverse(*mSnapshot->transform);
         }
-        mShader->setupProgram(mCaches.currentProgram, mModelView, *mSnapshot, &mTextureUnit);
+        mDrawModifiers.mShader->setupProgram(mCaches.currentProgram,
+                mModelView, *mSnapshot, &mTextureUnit);
     }
 }
 
 void OpenGLRenderer::setupDrawShaderIdentityUniforms() {
-    if (mShader) {
-        mShader->setupProgram(mCaches.currentProgram, mIdentity, *mSnapshot, &mTextureUnit);
+    if (mDrawModifiers.mShader) {
+        mDrawModifiers.mShader->setupProgram(mCaches.currentProgram,
+                mIdentity, *mSnapshot, &mTextureUnit);
     }
 }
 
 void OpenGLRenderer::setupDrawColorFilterUniforms() {
-    if (mColorFilter) {
-        mColorFilter->setupProgram(mCaches.currentProgram);
+    if (mDrawModifiers.mColorFilter) {
+        mDrawModifiers.mColorFilter->setupProgram(mCaches.currentProgram);
     }
 }
 
@@ -1726,21 +1782,24 @@
 // Drawing
 ///////////////////////////////////////////////////////////////////////////////
 
-status_t OpenGLRenderer::drawDisplayList(DisplayList* displayList,
-        Rect& dirty, int32_t flags, uint32_t level) {
-
+status_t OpenGLRenderer::drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t flags) {
     // All the usual checks and setup operations (quickReject, setupDraw, etc.)
     // will be performed by the display list itself
     if (displayList && displayList->isRenderable()) {
-        return displayList->replay(*this, dirty, flags, level);
+        if (CC_UNLIKELY(mDrawDeferDisabled)) {
+            return displayList->replay(*this, dirty, flags, 0);
+        }
+
+        DeferredDisplayList deferredList;
+        return displayList->replay(*this, dirty, flags, 0, &deferredList);
     }
 
     return DrawGlInfo::kStatusDone;
 }
 
-void OpenGLRenderer::outputDisplayList(DisplayList* displayList, uint32_t level) {
+void OpenGLRenderer::outputDisplayList(DisplayList* displayList) {
     if (displayList) {
-        displayList->output(level);
+        displayList->output(0);
     }
 }
 
@@ -1990,7 +2049,7 @@
     // Apply a scale transform on the canvas only when a shader is in use
     // Skia handles the ratio between the dst and src rects as a scale factor
     // when a shader is set
-    bool useScaleTransform = mShader && scaled;
+    bool useScaleTransform = mDrawModifiers.mShader && scaled;
     bool ignoreTransform = false;
 
     if (CC_LIKELY(mSnapshot->transform->isPureTranslate() && !useScaleTransform)) {
@@ -2445,15 +2504,15 @@
     //       if shader-based correction is enabled
     mCaches.dropShadowCache.setFontRenderer(fontRenderer);
     const ShadowTexture* shadow = mCaches.dropShadowCache.get(
-            paint, text, bytesCount, count, mShadowRadius, positions);
+            paint, text, bytesCount, count, mDrawModifiers.mShadowRadius, positions);
     const AutoTexture autoCleanup(shadow);
 
-    const float sx = x - shadow->left + mShadowDx;
-    const float sy = y - shadow->top + mShadowDy;
+    const float sx = x - shadow->left + mDrawModifiers.mShadowDx;
+    const float sy = y - shadow->top + mDrawModifiers.mShadowDy;
 
-    const int shadowAlpha = ((mShadowColor >> 24) & 0xFF) * mSnapshot->alpha;
-    int shadowColor = mShadowColor;
-    if (mShader) {
+    const int shadowAlpha = ((mDrawModifiers.mShadowColor >> 24) & 0xFF) * mSnapshot->alpha;
+    int shadowColor = mDrawModifiers.mShadowColor;
+    if (mDrawModifiers.mShader) {
         shadowColor = 0xffffffff;
     }
 
@@ -2501,7 +2560,7 @@
     SkXfermode::Mode mode;
     getAlphaAndMode(paint, &alpha, &mode);
 
-    if (CC_UNLIKELY(mHasShadow)) {
+    if (CC_UNLIKELY(mDrawModifiers.mHasShadow)) {
         drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer,
                 alpha, mode, 0.0f, 0.0f);
     }
@@ -2592,7 +2651,7 @@
     SkXfermode::Mode mode;
     getAlphaAndMode(paint, &alpha, &mode);
 
-    if (CC_UNLIKELY(mHasShadow)) {
+    if (CC_UNLIKELY(mDrawModifiers.mHasShadow)) {
         drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer, alpha, mode,
                 oldX, oldY);
     }
@@ -2748,8 +2807,8 @@
     mCaches.activeTexture(0);
 
     if (CC_LIKELY(!layer->region.isEmpty())) {
-        SkiaColorFilter* oldFilter = mColorFilter;
-        mColorFilter = layer->getColorFilter();
+        SkiaColorFilter* oldFilter = mDrawModifiers.mColorFilter;
+        mDrawModifiers.mColorFilter = layer->getColorFilter();
 
         if (layer->region.isRect()) {
             composeLayerRect(layer, layer->regionRect);
@@ -2788,7 +2847,7 @@
 #endif
         }
 
-        mColorFilter = oldFilter;
+        mDrawModifiers.mColorFilter = oldFilter;
 
         if (layer->debugDrawUpdate) {
             layer->debugDrawUpdate = false;
@@ -2809,13 +2868,13 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void OpenGLRenderer::resetShader() {
-    mShader = NULL;
+    mDrawModifiers.mShader = NULL;
 }
 
 void OpenGLRenderer::setupShader(SkiaShader* shader) {
-    mShader = shader;
-    if (mShader) {
-        mShader->set(&mCaches.textureCache, &mCaches.gradientCache);
+    mDrawModifiers.mShader = shader;
+    if (mDrawModifiers.mShader) {
+        mDrawModifiers.mShader->set(&mCaches.textureCache, &mCaches.gradientCache);
     }
 }
 
@@ -2824,11 +2883,11 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void OpenGLRenderer::resetColorFilter() {
-    mColorFilter = NULL;
+    mDrawModifiers.mColorFilter = NULL;
 }
 
 void OpenGLRenderer::setupColorFilter(SkiaColorFilter* filter) {
-    mColorFilter = filter;
+    mDrawModifiers.mColorFilter = filter;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -2836,15 +2895,15 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void OpenGLRenderer::resetShadow() {
-    mHasShadow = false;
+    mDrawModifiers.mHasShadow = false;
 }
 
 void OpenGLRenderer::setupShadow(float radius, float dx, float dy, int color) {
-    mHasShadow = true;
-    mShadowRadius = radius;
-    mShadowDx = dx;
-    mShadowDy = dy;
-    mShadowColor = color;
+    mDrawModifiers.mHasShadow = true;
+    mDrawModifiers.mShadowRadius = radius;
+    mDrawModifiers.mShadowDx = dx;
+    mDrawModifiers.mShadowDy = dy;
+    mDrawModifiers.mShadowColor = color;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -2852,22 +2911,23 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void OpenGLRenderer::resetPaintFilter() {
-    mHasDrawFilter = false;
+    mDrawModifiers.mHasDrawFilter = false;
 }
 
 void OpenGLRenderer::setupPaintFilter(int clearBits, int setBits) {
-    mHasDrawFilter = true;
-    mPaintFilterClearBits = clearBits & SkPaint::kAllFlags;
-    mPaintFilterSetBits = setBits & SkPaint::kAllFlags;
+    mDrawModifiers.mHasDrawFilter = true;
+    mDrawModifiers.mPaintFilterClearBits = clearBits & SkPaint::kAllFlags;
+    mDrawModifiers.mPaintFilterSetBits = setBits & SkPaint::kAllFlags;
 }
 
 SkPaint* OpenGLRenderer::filterPaint(SkPaint* paint) {
-    if (CC_LIKELY(!mHasDrawFilter || !paint)) return paint;
+    if (CC_LIKELY(!mDrawModifiers.mHasDrawFilter || !paint)) return paint;
 
     uint32_t flags = paint->getFlags();
 
     mFilteredPaint = *paint;
-    mFilteredPaint.setFlags((flags & ~mPaintFilterClearBits) | mPaintFilterSetBits);
+    mFilteredPaint.setFlags((flags & ~mDrawModifiers.mPaintFilterClearBits) |
+            mDrawModifiers.mPaintFilterSetBits);
 
     return &mFilteredPaint;
 }
@@ -2967,7 +3027,7 @@
 
     int color = paint->getColor();
     // If a shader is set, preserve only the alpha
-    if (mShader) {
+    if (mDrawModifiers.mShader) {
         color |= 0x00ffffff;
     }
     SkXfermode::Mode mode = getXfermode(paint->getXfermode());
@@ -3040,7 +3100,7 @@
 void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom,
         int color, SkXfermode::Mode mode, bool ignoreTransform) {
     // If a shader is set, preserve only the alpha
-    if (mShader) {
+    if (mDrawModifiers.mShader) {
         color |= 0x00ffffff;
     }
 
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index ad80d36..e12083c 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -48,6 +48,34 @@
 namespace android {
 namespace uirenderer {
 
+struct DrawModifiers {
+    SkiaShader* mShader;
+    SkiaColorFilter* mColorFilter;
+
+    // Drop shadow
+    bool mHasShadow;
+    float mShadowRadius;
+    float mShadowDx;
+    float mShadowDy;
+    int mShadowColor;
+
+    // Draw filters
+    bool mHasDrawFilter;
+    int mPaintFilterClearBits;
+    int mPaintFilterSetBits;
+};
+
+struct DeferredDisplayState {
+    Rect mBounds; // local bounds, mapped with matrix to be in screen space coordinates, clipped.
+    int mMultipliedAlpha; // -1 if invalid (because caching not set)
+
+    // the below are set and used by the OpenGLRenderer at record and deferred playback
+    Rect mClip;
+    mat4 mMatrix;
+    SkiaShader* mShader;
+    DrawModifiers mDrawModifiers;
+};
+
 ///////////////////////////////////////////////////////////////////////////////
 // Renderer
 ///////////////////////////////////////////////////////////////////////////////
@@ -182,9 +210,8 @@
     virtual bool clipRegion(SkRegion* region, SkRegion::Op op);
     virtual Rect* getClipRect();
 
-    virtual status_t drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t flags,
-            uint32_t level = 0);
-    virtual void outputDisplayList(DisplayList* displayList, uint32_t level = 0);
+    virtual status_t drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t flags);
+    virtual void outputDisplayList(DisplayList* displayList);
     virtual status_t drawLayer(Layer* layer, float x, float y, SkPaint* paint);
     virtual status_t drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
     virtual status_t drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint);
@@ -233,6 +260,23 @@
 
     SkPaint* filterPaint(SkPaint* paint);
 
+    bool disallowDeferral() {
+        // returns true if the OpenGLRenderer's state can be completely represented by
+        // a DeferredDisplayState object
+        return !mSnapshot->clipRegion->isEmpty() ||
+                mSnapshot->alpha < 1.0 ||
+                (mSnapshot->flags & Snapshot::kFlagIsLayer) ||
+                (mSnapshot->flags & Snapshot::kFlagFboTarget); // ensure we're not in a layer
+    }
+
+    bool disallowReorder() { return mDrawReorderDisabled; }
+
+    bool storeDisplayState(DeferredDisplayState& state);
+    void restoreDisplayState(const DeferredDisplayState& state);
+
+    const DrawModifiers& getDrawModifiers() { return mDrawModifiers; }
+    void setDrawModifiers(const DrawModifiers& drawModifiers) { mDrawModifiers = drawModifiers; }
+
     ANDROID_API bool isCurrentTransformSimple() {
         return mSnapshot->transform->isSimple();
     }
@@ -868,26 +912,11 @@
     // State used to define the clipping region
     sp<Snapshot> mTilingSnapshot;
 
-    // Shaders
-    SkiaShader* mShader;
-
-    // Color filters
-    SkiaColorFilter* mColorFilter;
-
     // Used to draw textured quads
     TextureVertex mMeshVertices[4];
 
-    // Drop shadow
-    bool mHasShadow;
-    float mShadowRadius;
-    float mShadowDx;
-    float mShadowDy;
-    int mShadowColor;
-
-    // Draw filters
-    bool mHasDrawFilter;
-    int mPaintFilterClearBits;
-    int mPaintFilterSetBits;
+    // shader, filters, and shadow
+    DrawModifiers mDrawModifiers;
     SkPaint mFilteredPaint;
 
     // Various caches
@@ -925,6 +954,8 @@
     // See PROPERTY_DISABLE_SCISSOR_OPTIMIZATION in
     // Properties.h
     bool mScissorOptimizationDisabled;
+    bool mDrawDeferDisabled;
+    bool mDrawReorderDisabled;
 
     // No-ops start/endTiling when set
     bool mSuppressTiling;
diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h
index cab0e54..ee7bf70 100644
--- a/libs/hwui/Patch.h
+++ b/libs/hwui/Patch.h
@@ -25,7 +25,6 @@
 
 #include "Rect.h"
 #include "Vertex.h"
-#include "utils/Compare.h"
 
 namespace android {
 namespace uirenderer {
diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp
index 8ee8f5c..62e38d3 100644
--- a/libs/hwui/PatchCache.cpp
+++ b/libs/hwui/PatchCache.cpp
@@ -42,6 +42,35 @@
 // Caching
 ///////////////////////////////////////////////////////////////////////////////
 
+int PatchCache::PatchDescription::compare(
+        const PatchCache::PatchDescription& lhs, const PatchCache::PatchDescription& rhs) {
+    int deltaInt = lhs.bitmapWidth - rhs.bitmapWidth;
+    if (deltaInt != 0) return deltaInt;
+
+    deltaInt = lhs.bitmapHeight - rhs.bitmapHeight;
+    if (deltaInt != 0) return deltaInt;
+
+    if (lhs.pixelWidth < rhs.pixelWidth) return -1;
+    if (lhs.pixelWidth > rhs.pixelWidth) return +1;
+
+    if (lhs.pixelHeight < rhs.pixelHeight) return -1;
+    if (lhs.pixelHeight > rhs.pixelHeight) return +1;
+
+    deltaInt = lhs.xCount - rhs.xCount;
+    if (deltaInt != 0) return deltaInt;
+
+    deltaInt = lhs.yCount - rhs.yCount;
+    if (deltaInt != 0) return deltaInt;
+
+    deltaInt = lhs.emptyCount - rhs.emptyCount;
+    if (deltaInt != 0) return deltaInt;
+
+    deltaInt = lhs.colorKey - rhs.colorKey;
+    if (deltaInt != 0) return deltaInt;
+
+    return 0;
+}
+
 void PatchCache::clear() {
     size_t count = mCache.size();
     for (size_t i = 0; i < count; i++) {
@@ -50,7 +79,7 @@
     mCache.clear();
 }
 
-Patch* PatchCache::get(const float bitmapWidth, const float bitmapHeight,
+Patch* PatchCache::get(const uint32_t bitmapWidth, const uint32_t bitmapHeight,
         const float pixelWidth, const float pixelHeight,
         const int32_t* xDivs, const int32_t* yDivs, const uint32_t* colors,
         const uint32_t width, const uint32_t height, const int8_t numColors) {
diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h
index 505798a..0822cba 100644
--- a/libs/hwui/PatchCache.h
+++ b/libs/hwui/PatchCache.h
@@ -19,7 +19,6 @@
 
 #include <utils/KeyedVector.h>
 
-#include "utils/Compare.h"
 #include "Debug.h"
 #include "Patch.h"
 
@@ -47,7 +46,7 @@
     PatchCache(uint32_t maxCapacity);
     ~PatchCache();
 
-    Patch* get(const float bitmapWidth, const float bitmapHeight,
+    Patch* get(const uint32_t bitmapWidth, const uint32_t bitmapHeight,
             const float pixelWidth, const float pixelHeight,
             const int32_t* xDivs, const int32_t* yDivs, const uint32_t* colors,
             const uint32_t width, const uint32_t height, const int8_t numColors);
@@ -70,7 +69,7 @@
                 xCount(0), yCount(0), emptyCount(0), colorKey(0) {
         }
 
-        PatchDescription(const float bitmapWidth, const float bitmapHeight,
+        PatchDescription(const uint32_t bitmapWidth, const uint32_t bitmapHeight,
                 const float pixelWidth, const float pixelHeight,
                 const uint32_t xCount, const uint32_t yCount,
                 const int8_t emptyCount, const uint32_t colorKey):
@@ -80,28 +79,29 @@
                 emptyCount(emptyCount), colorKey(colorKey) {
         }
 
-        bool operator<(const PatchDescription& rhs) const {
-            LTE_FLOAT(bitmapWidth) {
-                LTE_FLOAT(bitmapHeight) {
-                    LTE_FLOAT(pixelWidth) {
-                        LTE_FLOAT(pixelHeight) {
-                            LTE_INT(xCount) {
-                                LTE_INT(yCount) {
-                                    LTE_INT(emptyCount) {
-                                        LTE_INT(colorKey) return false;
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-            return false;
+        static int compare(const PatchDescription& lhs, const PatchDescription& rhs);
+
+        bool operator==(const PatchDescription& other) const {
+            return compare(*this, other) == 0;
+        }
+
+        bool operator!=(const PatchDescription& other) const {
+            return compare(*this, other) != 0;
+        }
+
+        friend inline int strictly_order_type(const PatchDescription& lhs,
+                const PatchDescription& rhs) {
+            return PatchDescription::compare(lhs, rhs) < 0;
+        }
+
+        friend inline int compare_type(const PatchDescription& lhs,
+                const PatchDescription& rhs) {
+            return PatchDescription::compare(lhs, rhs);
         }
 
     private:
-        float bitmapWidth;
-        float bitmapHeight;
+        uint32_t bitmapWidth;
+        uint32_t bitmapHeight;
         float pixelWidth;
         float pixelHeight;
         uint32_t xCount;
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index f0b5553..14a2376 100644
--- a/libs/hwui/Program.cpp
+++ b/libs/hwui/Program.cpp
@@ -160,7 +160,7 @@
         // up and to the left.
         // This offset value is based on an assumption that some hardware may use as
         // little as 12.4 precision, so we offset by slightly more than 1/16.
-        p.translate(.375, .375, 0);
+        p.translate(.065, .065, 0);
     }
 
     mat4 t(transformMatrix);
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 0c75242..f3957cf 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -82,6 +82,19 @@
  */
 #define PROPERTY_DISABLE_SCISSOR_OPTIMIZATION "ro.hwui.disable_scissor_opt"
 
+/**
+ * Disables draw operation deferral if set to "true", forcing draw
+ * commands to be issued to OpenGL in order, and processed in sequence
+ * with state-manipulation canvas commands.
+ */
+#define PROPERTY_DISABLE_DRAW_DEFER "debug.hwui.disable_draw_defer"
+
+/**
+ * Used to disable draw operation reordering when deferring draw operations
+ * Has no effect if PROPERTY_DISABLE_DRAW_DEFER is set to "true"
+ */
+#define PROPERTY_DISABLE_DRAW_REORDER "debug.hwui.disable_draw_reorder"
+
 // These properties are defined in mega-bytes
 #define PROPERTY_TEXTURE_CACHE_SIZE "ro.hwui.texture_cache_size"
 #define PROPERTY_LAYER_CACHE_SIZE "ro.hwui.layer_cache_size"
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index 5f4bb5a..f50ac3c 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -159,6 +159,13 @@
         bottom += dy;
     }
 
+    void outset(float delta) {
+        left -= delta;
+        top -= delta;
+        right += delta;
+        bottom += delta;
+    }
+
     void snapToPixelBoundaries() {
         left = floorf(left + 0.5f);
         top = floorf(top + 0.5f);
diff --git a/libs/hwui/utils/Compare.h b/libs/hwui/utils/Compare.h
deleted file mode 100644
index fdd9acf..0000000
--- a/libs/hwui/utils/Compare.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ANDROID_HWUI_COMPARE_H
-#define ANDROID_HWUI_COMPARE_H
-
-#include <cmath>
-
-/**
- * Compare floats.
- */
-#define LTE_FLOAT(a) \
-    if (a < rhs.a) return true; \
-    if (a == rhs.a)
-
-/**
- * Compare integers.
- */
-#define LTE_INT(a) \
-    if (a < rhs.a) return true; \
-    if (a == rhs.a)
-
-#endif // ANDROID_HWUI_COMPARE_H
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index f8c945b..86700b3 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -28,7 +28,6 @@
 #include "JNIHelp.h"
 
 #include <gui/Surface.h>
-#include <gui/SurfaceTextureClient.h>
 
 #include <media/ICrypto.h>
 #include <media/stagefright/MediaCodec.h>
@@ -114,9 +113,9 @@
         const sp<IGraphicBufferProducer> &bufferProducer,
         const sp<ICrypto> &crypto,
         int flags) {
-    sp<SurfaceTextureClient> client;
+    sp<Surface> client;
     if (bufferProducer != NULL) {
-        mSurfaceTextureClient = new SurfaceTextureClient(bufferProducer);
+        mSurfaceTextureClient = new Surface(bufferProducer);
     } else {
         mSurfaceTextureClient.clear();
     }
@@ -398,7 +397,7 @@
     if (jsurface != NULL) {
         sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
         if (surface != NULL) {
-            bufferProducer = surface->getSurfaceTexture();
+            bufferProducer = surface->getIGraphicBufferProducer();
         } else {
             jniThrowException(
                     env,
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index a9bb9af..f478788 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -32,7 +32,7 @@
 struct ICrypto;
 struct IGraphicBufferProducer;
 struct MediaCodec;
-struct SurfaceTextureClient;
+class Surface;
 
 struct JMediaCodec : public RefBase {
     JMediaCodec(
@@ -91,7 +91,7 @@
 private:
     jclass mClass;
     jweak mObject;
-    sp<SurfaceTextureClient> mSurfaceTextureClient;
+    sp<Surface> mSurfaceTextureClient;
 
     sp<ALooper> mLooper;
     sp<MediaCodec> mCodec;
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 5408a1f..7421022 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -273,7 +273,7 @@
     if (jsurface) {
         sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
         if (surface != NULL) {
-            new_st = surface->getSurfaceTexture();
+            new_st = surface->getIGraphicBufferProducer();
             if (new_st == NULL) {
                 jniThrowException(env, "java/lang/IllegalArgumentException",
                     "The surface does not have a binding SurfaceTexture!");
diff --git a/media/mca/filterfw/jni/jni_gl_environment.cpp b/media/mca/filterfw/jni/jni_gl_environment.cpp
index b64004e..9abf191 100644
--- a/media/mca/filterfw/jni/jni_gl_environment.cpp
+++ b/media/mca/filterfw/jni/jni_gl_environment.cpp
@@ -24,7 +24,7 @@
 #include "native/core/gl_env.h"
 
 #include <gui/IGraphicBufferProducer.h>
-#include <gui/SurfaceTextureClient.h>
+#include <gui/Surface.h>
 #include <utils/Errors.h>
 #include <system/window.h>
 
@@ -34,7 +34,7 @@
 using android::MediaRecorder;
 using android::sp;
 using android::IGraphicBufferProducer;
-using android::SurfaceTextureClient;
+using android::Surface;
 
 
 class NativeWindowHandle : public WindowHandle {
@@ -290,7 +290,7 @@
               <IGraphicBufferProducer> handle.");
       return -1;
     }
-    sp<SurfaceTextureClient> surfaceTC = new SurfaceTextureClient(surfaceMS);
+    sp<Surface> surfaceTC = new Surface(surfaceMS);
     // Get the ANativeWindow
     sp<ANativeWindow> window = surfaceTC;
 
diff --git a/media/mca/filterfw/native/core/gl_env.cpp b/media/mca/filterfw/native/core/gl_env.cpp
index 8ed82e2..73768fe 100644
--- a/media/mca/filterfw/native/core/gl_env.cpp
+++ b/media/mca/filterfw/native/core/gl_env.cpp
@@ -161,7 +161,7 @@
 
   // Create dummy surface using a GLConsumer
   surfaceTexture_ = new GLConsumer(0);
-  window_ = new SurfaceTextureClient(static_cast<sp<IGraphicBufferProducer> >(
+  window_ = new Surface(static_cast<sp<IGraphicBufferProducer> >(
           surfaceTexture_->getBufferQueue()));
 
   surfaces_[0] = SurfaceWindowPair(eglCreateWindowSurface(display(), config, window_.get(), NULL), NULL);
diff --git a/media/mca/filterfw/native/core/gl_env.h b/media/mca/filterfw/native/core/gl_env.h
index 3c87195..81e1e9d 100644
--- a/media/mca/filterfw/native/core/gl_env.h
+++ b/media/mca/filterfw/native/core/gl_env.h
@@ -28,7 +28,7 @@
 #include <EGL/egl.h>
 
 #include <gui/IGraphicBufferProducer.h>
-#include <gui/SurfaceTextureClient.h>
+#include <gui/Surface.h>
 
 namespace android {
 namespace filterfw {
diff --git a/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java b/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java
index a6415b2..89f84fc 100644
--- a/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java
+++ b/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java
@@ -4,6 +4,7 @@
 import android.app.backup.FullBackup;
 import android.app.backup.FullBackupDataOutput;
 import android.content.Context;
+import android.os.Environment;
 import android.os.ParcelFileDescriptor;
 import android.os.storage.StorageManager;
 import android.os.storage.StorageVolume;
@@ -11,6 +12,7 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.util.HashSet;
 
 public class SharedStorageAgent extends FullBackupAgent {
     static final String TAG = "SharedStorageAgent";
@@ -38,13 +40,20 @@
         // "primary" shared storage volume is first in the list.
         if (mVolumes != null) {
             if (DEBUG) Slog.i(TAG, "Backing up " + mVolumes.length + " shared volumes");
+            // Ignore all apps' getExternalFilesDir() content; it is backed up as part of
+            // each app-specific payload.
+            HashSet<String> externalFilesDirFilter = new HashSet<String>();
+            final File externalAndroidRoot = new File(Environment.getExternalStorageDirectory(),
+                    Environment.DIRECTORY_ANDROID);
+            externalFilesDirFilter.add(externalAndroidRoot.getCanonicalPath());
+
             for (int i = 0; i < mVolumes.length; i++) {
                 StorageVolume v = mVolumes[i];
                 // Express the contents of volume N this way in the tar stream:
                 //     shared/N/path/to/file
                 // The restore will then extract to the given volume
                 String domain = FullBackup.SHARED_PREFIX + i;
-                fullBackupFileTree(null, domain, v.getPath(), null, output);
+                fullBackupFileTree(null, domain, v.getPath(), externalFilesDirFilter, output);
             }
         }
     }
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index a03ca33..a58c9a0 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -196,7 +196,7 @@
     <string name="quick_settings_wifi_label" msgid="9135344704899546041">"Wi-Fi"</string>
     <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_off_label" msgid="7558778100843885864">"Wi-Fi av"</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_brightness_dialog_title" msgid="8599674057673605368">"Ljusstyrka"</string>
diff --git a/services/input/SpriteController.cpp b/services/input/SpriteController.cpp
index 1f3d2cf..3c3b919 100644
--- a/services/input/SpriteController.cpp
+++ b/services/input/SpriteController.cpp
@@ -22,12 +22,14 @@
 
 #include <cutils/log.h>
 #include <utils/String8.h>
+#include <gui/Surface.h>
 
 #include <SkBitmap.h>
 #include <SkCanvas.h>
 #include <SkColor.h>
 #include <SkPaint.h>
 #include <SkXfermode.h>
+#include <android/native_window.h>
 
 namespace android {
 
@@ -197,16 +199,16 @@
         if (update.state.surfaceControl != NULL && !update.state.surfaceDrawn
                 && update.state.wantSurfaceVisible()) {
             sp<Surface> surface = update.state.surfaceControl->getSurface();
-            Surface::SurfaceInfo surfaceInfo;
-            status_t status = surface->lock(&surfaceInfo);
+            ANativeWindow_Buffer outBuffer;
+            status_t status = surface->lock(&outBuffer, NULL);
             if (status) {
                 ALOGE("Error %d locking sprite surface before drawing.", status);
             } else {
                 SkBitmap surfaceBitmap;
-                ssize_t bpr = surfaceInfo.s * bytesPerPixel(surfaceInfo.format);
+                ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
                 surfaceBitmap.setConfig(SkBitmap::kARGB_8888_Config,
-                        surfaceInfo.w, surfaceInfo.h, bpr);
-                surfaceBitmap.setPixels(surfaceInfo.bits);
+                        outBuffer.width, outBuffer.height, bpr);
+                surfaceBitmap.setPixels(outBuffer.bits);
 
                 SkCanvas surfaceCanvas;
                 surfaceCanvas.setBitmapDevice(surfaceBitmap);
@@ -215,15 +217,15 @@
                 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
                 surfaceCanvas.drawBitmap(update.state.icon.bitmap, 0, 0, &paint);
 
-                if (surfaceInfo.w > uint32_t(update.state.icon.bitmap.width())) {
+                if (outBuffer.width > uint32_t(update.state.icon.bitmap.width())) {
                     paint.setColor(0); // transparent fill color
                     surfaceCanvas.drawRectCoords(update.state.icon.bitmap.width(), 0,
-                            surfaceInfo.w, update.state.icon.bitmap.height(), paint);
+                            outBuffer.width, update.state.icon.bitmap.height(), paint);
                 }
-                if (surfaceInfo.h > uint32_t(update.state.icon.bitmap.height())) {
+                if (outBuffer.height > uint32_t(update.state.icon.bitmap.height())) {
                     paint.setColor(0); // transparent fill color
                     surfaceCanvas.drawRectCoords(0, update.state.icon.bitmap.height(),
-                            surfaceInfo.w, surfaceInfo.h, paint);
+                            outBuffer.width, outBuffer.height, paint);
                 }
 
                 status = surface->unlockAndPost();
@@ -371,8 +373,7 @@
     sp<SurfaceControl> surfaceControl = mSurfaceComposerClient->createSurface(
             String8("Sprite"), width, height, PIXEL_FORMAT_RGBA_8888,
             ISurfaceComposerClient::eHidden);
-    if (surfaceControl == NULL || !surfaceControl->isValid()
-            || !surfaceControl->getSurface()->isValid()) {
+    if (surfaceControl == NULL || !surfaceControl->isValid()) {
         ALOGE("Error creating sprite surface.");
         return NULL;
     }
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 1a8641b..6ba5cff 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -731,7 +731,8 @@
         }
     }
 
-    private void resetAllInternalStateLocked(final boolean updateOnlyWhenLocaleChanged) {
+    private void resetAllInternalStateLocked(final boolean updateOnlyWhenLocaleChanged,
+            final boolean resetDefaultEnabledIme) {
         if (!mSystemReady) {
             // not system ready
             return;
@@ -749,8 +750,7 @@
             }
             // InputMethodAndSubtypeListManager should be reset when the locale is changed.
             mImListManager = new InputMethodAndSubtypeListManager(mContext, this);
-            buildInputMethodListLocked(mMethodList, mMethodMap,
-                    updateOnlyWhenLocaleChanged /* resetDefaultEnabledIme */);
+            buildInputMethodListLocked(mMethodList, mMethodMap, resetDefaultEnabledIme);
             if (!updateOnlyWhenLocaleChanged) {
                 final String selectedImiId = mSettings.getSelectedInputMethod();
                 if (TextUtils.isEmpty(selectedImiId)) {
@@ -775,14 +775,21 @@
     }
 
     private void checkCurrentLocaleChangedLocked() {
-        resetAllInternalStateLocked(true);
+        resetAllInternalStateLocked(true /* updateOnlyWhenLocaleChanged */,
+                true /* resetDefaultImeLocked */);
     }
 
     private void switchUserLocked(int newUserId) {
         mSettings.setCurrentUserId(newUserId);
         // InputMethodFileManager should be reset when the user is changed
         mFileManager = new InputMethodFileManager(mMethodMap, newUserId);
-        resetAllInternalStateLocked(false);
+        final String defaultImiId = mSettings.getSelectedInputMethod();
+        final boolean needsToResetDefaultIme = TextUtils.isEmpty(defaultImiId);
+        if (DEBUG) {
+            Slog.d(TAG, "Switch user: " + newUserId + " current ime = " + defaultImiId);
+        }
+        resetAllInternalStateLocked(false  /* updateOnlyWhenLocaleChanged */,
+                needsToResetDefaultIme);
     }
 
     @Override
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index e5cba62..9c518a1 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -718,7 +718,7 @@
         synchronized (mLock) {
             // Disconnect from services for the old user.
             UserState oldUserState = getUserStateLocked(mCurrentUserId);
-            unbindAllServicesLocked(oldUserState);
+            oldUserState.onSwitchToAnotherUser();
 
             // Disable the local managers for the old user.
             if (oldUserState.mClients.getRegisteredCallbackCount() > 0) {
@@ -737,11 +737,14 @@
             if (userState.mUiAutomationService != null) {
                 // Switching users disables the UI automation service.
                 userState.mUiAutomationService.binderDied();
-            } else if (readConfigurationForUserStateLocked(userState)) {
-                // Update the user state if needed.
-               onUserStateChangedLocked(userState);
             }
 
+            readConfigurationForUserStateLocked(userState);
+            // Even if reading did not yield change, we have to update
+            // the state since the context in which the current user
+            // state was used has changed since it was inactive.
+            onUserStateChangedLocked(userState);
+
             if (announceNewUser) {
                 // Schedule announcement of the current user if needed.
                 mMainHandler.sendEmptyMessageDelayed(MainHandler.MSG_ANNOUNCE_NEW_USER_IF_NEEDED,
@@ -1640,14 +1643,18 @@
 
             if (mIsAutomation || info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion
                     >= Build.VERSION_CODES.JELLY_BEAN) {
-                mFetchFlags |= (info.flags
-                        & AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0 ?
-                                AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS : 0;
+                if ((info.flags & AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0) {
+                    mFetchFlags |= AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
+                } else {
+                    mFetchFlags &= ~AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
+                }
             }
 
-            mFetchFlags |= (info.flags
-                    & AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS) != 0 ?
-                            AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS : 0;
+            if ((info.flags & AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS) != 0) {
+                mFetchFlags |= AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS;
+            } else {
+                mFetchFlags &= ~AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS;
+            }
 
             if (mResolveInfo != null) {
                 mRequestTouchExplorationMode = (info.flags
@@ -2561,11 +2568,21 @@
     private class UserState {
         public final int mUserId;
 
-        public final CopyOnWriteArrayList<Service> mBoundServices = new CopyOnWriteArrayList<Service>();
+        // Non-transient state.
 
         public final RemoteCallbackList<IAccessibilityManagerClient> mClients =
             new RemoteCallbackList<IAccessibilityManagerClient>();
 
+        public final SparseArray<AccessibilityConnectionWrapper> mInteractionConnections =
+                new SparseArray<AccessibilityConnectionWrapper>();
+
+        public final SparseArray<IBinder> mWindowTokens = new SparseArray<IBinder>();
+
+        // Transient state.
+
+        public final CopyOnWriteArrayList<Service> mBoundServices =
+                new CopyOnWriteArrayList<Service>();
+
         public final Map<ComponentName, Service> mComponentNameToServiceMap =
                 new HashMap<ComponentName, Service>();
 
@@ -2579,15 +2596,9 @@
         public final Set<ComponentName> mTouchExplorationGrantedServices =
                 new HashSet<ComponentName>();
 
-        public final SparseArray<AccessibilityConnectionWrapper>
-                mInteractionConnections =
-                new SparseArray<AccessibilityConnectionWrapper>();
-
-        public final SparseArray<IBinder> mWindowTokens = new SparseArray<IBinder>();
-
         public int mHandledFeedbackTypes = 0;
 
-        public int mLastSentClientState;
+        public int mLastSentClientState = -1;
 
         public boolean mIsAccessibilityEnabled;
         public boolean mIsTouchExplorationEnabled;
@@ -2612,6 +2623,34 @@
             }
             return clientState;
         }
+
+        public void onSwitchToAnotherUser() {
+            // Clear UI test automation state.
+            if (mUiAutomationService != null) {
+                mUiAutomationService.binderDied();
+                mUiAutomationService = null;
+                mUiAutomationServiceClient = null;
+            }
+
+            // Unbind all services.
+            unbindAllServicesLocked(this);
+
+            // Clear service management state.
+            mBoundServices.clear();
+            mBindingServices.clear();
+
+            // Clear event management state.
+            mHandledFeedbackTypes = 0;
+            mLastSentClientState = -1;
+
+            // Clear state persisted in settings.
+            mEnabledServices.clear();
+            mTouchExplorationGrantedServices.clear();
+            mIsAccessibilityEnabled = false;
+            mIsTouchExplorationEnabled = false;
+            mIsEnhancedWebAccessibilityEnabled = false;
+            mIsDisplayMagnificationEnabled = false;
+        }
     }
 
     private final class AccessibilityContentObserver extends ContentObserver {
diff --git a/services/java/com/android/server/power/ElectronBeam.java b/services/java/com/android/server/power/ElectronBeam.java
index 2828e5e..e302e83 100644
--- a/services/java/com/android/server/power/ElectronBeam.java
+++ b/services/java/com/android/server/power/ElectronBeam.java
@@ -713,17 +713,17 @@
      */
     private static final class NaturalSurfaceLayout implements DisplayTransactionListener {
         private final DisplayManagerService mDisplayManager;
-        private SurfaceControl mSurface;
+        private SurfaceControl mSurfaceControl;
 
-        public NaturalSurfaceLayout(DisplayManagerService displayManager, SurfaceControl surface) {
+        public NaturalSurfaceLayout(DisplayManagerService displayManager, SurfaceControl surfaceControl) {
             mDisplayManager = displayManager;
-            mSurface = surface;
+            mSurfaceControl = surfaceControl;
             mDisplayManager.registerDisplayTransactionListener(this);
         }
 
         public void dispose() {
             synchronized (this) {
-                mSurface = null;
+                mSurfaceControl = null;
             }
             mDisplayManager.unregisterDisplayTransactionListener(this);
         }
@@ -731,27 +731,27 @@
         @Override
         public void onDisplayTransaction() {
             synchronized (this) {
-                if (mSurface == null) {
+                if (mSurfaceControl == null) {
                     return;
                 }
 
                 DisplayInfo displayInfo = mDisplayManager.getDisplayInfo(Display.DEFAULT_DISPLAY);
                 switch (displayInfo.rotation) {
                     case Surface.ROTATION_0:
-                        mSurface.setPosition(0, 0);
-                        mSurface.setMatrix(1, 0, 0, 1);
+                        mSurfaceControl.setPosition(0, 0);
+                        mSurfaceControl.setMatrix(1, 0, 0, 1);
                         break;
                     case Surface.ROTATION_90:
-                        mSurface.setPosition(0, displayInfo.logicalHeight);
-                        mSurface.setMatrix(0, -1, 1, 0);
+                        mSurfaceControl.setPosition(0, displayInfo.logicalHeight);
+                        mSurfaceControl.setMatrix(0, -1, 1, 0);
                         break;
                     case Surface.ROTATION_180:
-                        mSurface.setPosition(displayInfo.logicalWidth, displayInfo.logicalHeight);
-                        mSurface.setMatrix(-1, 0, 0, -1);
+                        mSurfaceControl.setPosition(displayInfo.logicalWidth, displayInfo.logicalHeight);
+                        mSurfaceControl.setMatrix(-1, 0, 0, -1);
                         break;
                     case Surface.ROTATION_270:
-                        mSurface.setPosition(displayInfo.logicalWidth, 0);
-                        mSurface.setMatrix(0, 1, -1, 0);
+                        mSurfaceControl.setPosition(displayInfo.logicalWidth, 0);
+                        mSurfaceControl.setMatrix(0, 1, -1, 0);
                         break;
                 }
             }
diff --git a/services/java/com/android/server/wm/AppWindowToken.java b/services/java/com/android/server/wm/AppWindowToken.java
index 8fd5209..8cc1d02 100644
--- a/services/java/com/android/server/wm/AppWindowToken.java
+++ b/services/java/com/android/server/wm/AppWindowToken.java
@@ -157,7 +157,7 @@
                         + win.isDrawnLw()
                         + ", isAnimating=" + win.mWinAnimator.isAnimating());
                 if (!win.isDrawnLw()) {
-                    Slog.v(WindowManagerService.TAG, "Not displayed: s=" + win.mWinAnimator.mSurface
+                    Slog.v(WindowManagerService.TAG, "Not displayed: s=" + win.mWinAnimator.mSurfaceControl
                             + " pv=" + win.mPolicyVisibility
                             + " mDrawState=" + win.mWinAnimator.mDrawState
                             + " ah=" + win.mAttachedHidden
diff --git a/services/java/com/android/server/wm/BlackFrame.java b/services/java/com/android/server/wm/BlackFrame.java
index a197840..774b165 100644
--- a/services/java/com/android/server/wm/BlackFrame.java
+++ b/services/java/com/android/server/wm/BlackFrame.java
@@ -37,24 +37,22 @@
         final SurfaceControl surface;
 
         BlackSurface(SurfaceSession session, int layer, int l, int t, int r, int b, int layerStack)
-                throws Surface.OutOfResourcesException {
+                throws SurfaceControl.OutOfResourcesException {
             left = l;
             top = t;
             this.layer = layer;
             int w = r-l;
             int h = b-t;
-            try {
-                if (WindowManagerService.DEBUG_SURFACE_TRACE) {
-                    surface = new WindowStateAnimator.SurfaceTrace(session, "BlackSurface("
-                            + l + ", " + t + ")",
-                            w, h, PixelFormat.OPAQUE, SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN);
-                } else {
-                    surface = new SurfaceControl(session, "BlackSurface",
-                            w, h, PixelFormat.OPAQUE, SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN);
-                }
-            } catch (SurfaceControl.OutOfResourcesException e) {
-                throw new Surface.OutOfResourcesException(e.getMessage());
+
+            if (WindowManagerService.DEBUG_SURFACE_TRACE) {
+                surface = new WindowStateAnimator.SurfaceTrace(session, "BlackSurface("
+                        + l + ", " + t + ")",
+                        w, h, PixelFormat.OPAQUE, SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN);
+            } else {
+                surface = new SurfaceControl(session, "BlackSurface",
+                        w, h, PixelFormat.OPAQUE, SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN);
             }
+
             surface.setAlpha(1);
             surface.setLayerStack(layerStack);
             surface.setLayer(layer);
@@ -109,7 +107,7 @@
     }
 
     public BlackFrame(SurfaceSession session, Rect outer, Rect inner,
-            int layer, final int layerStack) throws Surface.OutOfResourcesException {
+            int layer, final int layerStack) throws SurfaceControl.OutOfResourcesException {
         boolean success = false;
 
         mOuterRect = new Rect(outer);
diff --git a/services/java/com/android/server/wm/DisplayMagnifier.java b/services/java/com/android/server/wm/DisplayMagnifier.java
index 55c6fa6..0f51028 100644
--- a/services/java/com/android/server/wm/DisplayMagnifier.java
+++ b/services/java/com/android/server/wm/DisplayMagnifier.java
@@ -491,15 +491,15 @@
             private boolean mInvalidated;
 
             public ViewportWindow(Context context) {
-                SurfaceControl surface = null;
+                SurfaceControl surfaceControl = null;
                 try {
                     mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
-                    surface = new SurfaceControl(mWindowManagerService.mFxSession, SURFACE_TITLE,
+                    surfaceControl = new SurfaceControl(mWindowManagerService.mFxSession, SURFACE_TITLE,
                             mTempPoint.x, mTempPoint.y, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
                 } catch (SurfaceControl.OutOfResourcesException oore) {
                     /* ignore */
                 }
-                mSurfaceControl = surface;
+                mSurfaceControl = surfaceControl;
                 mSurfaceControl.setLayerStack(mWindowManager.getDefaultDisplay().getLayerStack());
                 mSurfaceControl.setLayer(mWindowManagerService.mPolicy.windowTypeToLayerLw(
                         WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY)
diff --git a/services/java/com/android/server/wm/DragState.java b/services/java/com/android/server/wm/DragState.java
index 63f306f..745b886 100644
--- a/services/java/com/android/server/wm/DragState.java
+++ b/services/java/com/android/server/wm/DragState.java
@@ -46,7 +46,7 @@
 class DragState {
     final WindowManagerService mService;
     IBinder mToken;
-    SurfaceControl mSurface;
+    SurfaceControl mSurfaceControl;
     int mFlags;
     IBinder mLocalWin;
     ClipData mData;
@@ -69,17 +69,17 @@
             int flags, IBinder localWin) {
         mService = service;
         mToken = token;
-        mSurface = surface;
+        mSurfaceControl = surface;
         mFlags = flags;
         mLocalWin = localWin;
         mNotifiedWindows = new ArrayList<WindowState>();
     }
 
     void reset() {
-        if (mSurface != null) {
-            mSurface.destroy();
+        if (mSurfaceControl != null) {
+            mSurfaceControl.destroy();
         }
-        mSurface = null;
+        mSurfaceControl = null;
         mFlags = 0;
         mLocalWin = null;
         mToken = null;
@@ -296,9 +296,9 @@
                 WindowManagerService.TAG, ">>> OPEN TRANSACTION notifyMoveLw");
         SurfaceControl.openTransaction();
         try {
-            mSurface.setPosition(x - mThumbOffsetX, y - mThumbOffsetY);
+            mSurfaceControl.setPosition(x - mThumbOffsetX, y - mThumbOffsetY);
             if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, "  DRAG "
-                    + mSurface + ": pos=(" +
+                    + mSurfaceControl + ": pos=(" +
                     (int)(x - mThumbOffsetX) + "," + (int)(y - mThumbOffsetY) + ")");
         } finally {
             SurfaceControl.closeTransaction();
diff --git a/services/java/com/android/server/wm/ScreenRotationAnimation.java b/services/java/com/android/server/wm/ScreenRotationAnimation.java
index 05397ac..5d4ab56 100644
--- a/services/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -44,7 +44,7 @@
 
     final Context mContext;
     final Display mDisplay;
-    SurfaceControl mSurface;
+    SurfaceControl mSurfaceControl;
     BlackFrame mCustomBlackFrame;
     BlackFrame mExitingBlackFrame;
     BlackFrame mEnteringBlackFrame;
@@ -128,7 +128,7 @@
     long mHalfwayPoint;
 
     public void printTo(String prefix, PrintWriter pw) {
-        pw.print(prefix); pw.print("mSurface="); pw.print(mSurface);
+        pw.print(prefix); pw.print("mSurface="); pw.print(mSurfaceControl);
                 pw.print(" mWidth="); pw.print(mWidth);
                 pw.print(" mHeight="); pw.println(mHeight);
         if (USE_CUSTOM_BLACK_FRAME) {
@@ -220,25 +220,25 @@
         try {
             try {
                 if (WindowManagerService.DEBUG_SURFACE_TRACE) {
-                    mSurface = new SurfaceTrace(session, "FreezeSurface",
+                    mSurfaceControl = new SurfaceTrace(session, "FreezeSurface",
                             mWidth, mHeight,
                             PixelFormat.OPAQUE, SurfaceControl.FX_SURFACE_SCREENSHOT | SurfaceControl.HIDDEN);
                 } else {
-                    mSurface = new SurfaceControl(session, "FreezeSurface",
+                    mSurfaceControl = new SurfaceControl(session, "FreezeSurface",
                             mWidth, mHeight,
                             PixelFormat.OPAQUE, SurfaceControl.FX_SURFACE_SCREENSHOT | SurfaceControl.HIDDEN);
                 }
-                mSurface.setLayerStack(mDisplay.getLayerStack());
-                mSurface.setLayer(FREEZE_LAYER + 1);
-                mSurface.setAlpha(0);
-                mSurface.show();
+                mSurfaceControl.setLayerStack(mDisplay.getLayerStack());
+                mSurfaceControl.setLayer(FREEZE_LAYER + 1);
+                mSurfaceControl.setAlpha(0);
+                mSurfaceControl.show();
             } catch (SurfaceControl.OutOfResourcesException e) {
                 Slog.w(TAG, "Unable to allocate freeze surface", e);
             }
 
             if (WindowManagerService.SHOW_TRANSACTIONS ||
                     WindowManagerService.SHOW_SURFACE_ALLOC) Slog.i(WindowManagerService.TAG,
-                            "  FREEZE " + mSurface + ": CREATE");
+                            "  FREEZE " + mSurfaceControl + ": CREATE");
 
             setRotationInTransaction(originalRotation);
         } finally {
@@ -251,7 +251,7 @@
     }
 
     boolean hasScreenshot() {
-        return mSurface != null;
+        return mSurfaceControl != null;
     }
 
     static int deltaRotation(int oldRotation, int newRotation) {
@@ -261,13 +261,13 @@
     }
 
     private void setSnapshotTransformInTransaction(Matrix matrix, float alpha) {
-        if (mSurface != null) {
+        if (mSurfaceControl != null) {
             matrix.getValues(mTmpFloats);
-            mSurface.setPosition(mTmpFloats[Matrix.MTRANS_X], mTmpFloats[Matrix.MTRANS_Y]);
-            mSurface.setMatrix(
+            mSurfaceControl.setPosition(mTmpFloats[Matrix.MTRANS_X], mTmpFloats[Matrix.MTRANS_Y]);
+            mSurfaceControl.setMatrix(
                     mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
                     mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
-            mSurface.setAlpha(alpha);
+            mSurfaceControl.setAlpha(alpha);
             if (DEBUG_TRANSFORMS) {
                 float[] srcPnts = new float[] { 0, 0, mWidth, mHeight };
                 float[] dstPnts = new float[4];
@@ -333,7 +333,7 @@
      */
     private boolean startAnimation(SurfaceSession session, long maxAnimationDuration,
             float animationScale, int finalWidth, int finalHeight, boolean dismissing) {
-        if (mSurface == null) {
+        if (mSurfaceControl == null) {
             // Can't do animation.
             return false;
         }
@@ -509,7 +509,7 @@
                 mCustomBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER + 3,
                         layerStack);
                 mCustomBlackFrame.setMatrix(mFrameInitialMatrix);
-            } catch (Surface.OutOfResourcesException e) {
+            } catch (SurfaceControl.OutOfResourcesException e) {
                 Slog.w(TAG, "Unable to allocate black surface", e);
             } finally {
                 SurfaceControl.closeTransaction();
@@ -539,7 +539,7 @@
                 mExitingBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER + 2,
                         layerStack);
                 mExitingBlackFrame.setMatrix(mFrameInitialMatrix);
-            } catch (Surface.OutOfResourcesException e) {
+            } catch (SurfaceControl.OutOfResourcesException e) {
                 Slog.w(TAG, "Unable to allocate black surface", e);
             } finally {
                 SurfaceControl.closeTransaction();
@@ -561,7 +561,7 @@
                 Rect inner = new Rect(0, 0, finalWidth, finalHeight);
                 mEnteringBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER,
                         layerStack);
-            } catch (Surface.OutOfResourcesException e) {
+            } catch (SurfaceControl.OutOfResourcesException e) {
                 Slog.w(TAG, "Unable to allocate black surface", e);
             } finally {
                 SurfaceControl.closeTransaction();
@@ -580,7 +580,7 @@
     public boolean dismiss(SurfaceSession session, long maxAnimationDuration,
             float animationScale, int finalWidth, int finalHeight) {
         if (DEBUG_STATE) Slog.v(TAG, "Dismiss!");
-        if (mSurface == null) {
+        if (mSurfaceControl == null) {
             // Can't do animation.
             return false;
         }
@@ -598,12 +598,12 @@
 
     public void kill() {
         if (DEBUG_STATE) Slog.v(TAG, "Kill!");
-        if (mSurface != null) {
+        if (mSurfaceControl != null) {
             if (WindowManagerService.SHOW_TRANSACTIONS ||
                     WindowManagerService.SHOW_SURFACE_ALLOC) Slog.i(WindowManagerService.TAG,
-                            "  FREEZE " + mSurface + ": DESTROY");
-            mSurface.destroy();
-            mSurface = null;
+                            "  FREEZE " + mSurfaceControl + ": DESTROY");
+            mSurfaceControl.destroy();
+            mSurfaceControl = null;
         }
         if (mCustomBlackFrame != null) {
             mCustomBlackFrame.kill();
@@ -861,10 +861,10 @@
             return;
         }
 
-        if (mSurface != null) {
+        if (mSurfaceControl != null) {
             if (!mMoreStartExit && !mMoreFinishExit && !mMoreRotateExit) {
                 if (DEBUG_STATE) Slog.v(TAG, "Exit animations done, hiding screenshot surface");
-                mSurface.hide();
+                mSurfaceControl.hide();
             }
         }
 
diff --git a/services/java/com/android/server/wm/Session.java b/services/java/com/android/server/wm/Session.java
index 7a0fa16..e82068c 100644
--- a/services/java/com/android/server/wm/Session.java
+++ b/services/java/com/android/server/wm/Session.java
@@ -314,17 +314,17 @@
             mService.mDragState.mThumbOffsetY = thumbCenterY;
 
             // Make the surface visible at the proper location
-            final SurfaceControl surface = mService.mDragState.mSurface;
+            final SurfaceControl surfaceControl = mService.mDragState.mSurfaceControl;
             if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(
                     WindowManagerService.TAG, ">>> OPEN TRANSACTION performDrag");
             SurfaceControl.openTransaction();
             try {
-                surface.setPosition(touchX - thumbCenterX,
+                surfaceControl.setPosition(touchX - thumbCenterX,
                         touchY - thumbCenterY);
-                surface.setAlpha(.7071f);
-                surface.setLayer(mService.mDragState.getDragLayerLw());
-                surface.setLayerStack(display.getLayerStack());
-                surface.show();
+                surfaceControl.setAlpha(.7071f);
+                surfaceControl.setLayer(mService.mDragState.getDragLayerLw());
+                surfaceControl.setLayerStack(display.getLayerStack());
+                surfaceControl.show();
             } finally {
                 SurfaceControl.closeTransaction();
                 if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index a2c44ef..67daf75 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -225,7 +225,7 @@
             WindowStateAnimator winAnimator = win.mWinAnimator;
             final int flags = winAnimator.mAttrFlags;
 
-            if (winAnimator.mSurface != null) {
+            if (winAnimator.mSurfaceControl != null) {
                 final boolean wasAnimating = winAnimator.mWasAnimating;
                 final boolean nowAnimating = winAnimator.stepAnimationLocked(mCurrentTime);
 
@@ -373,7 +373,7 @@
         for (int i = windows.size() - 1; i >= 0; i--) {
             final WindowState win = windows.get(i);
             WindowStateAnimator winAnimator = win.mWinAnimator;
-            if (winAnimator.mSurface == null) {
+            if (winAnimator.mSurfaceControl == null) {
                 continue;
             }
 
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 7b83c24..d38273d 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -1112,7 +1112,7 @@
             if (DEBUG_INPUT_METHOD) {
                 Slog.i(TAG, "isVisibleOrAdding " + w + ": " + w.isVisibleOrAdding());
                 if (!w.isVisibleOrAdding()) {
-                    Slog.i(TAG, "  mSurface=" + w.mWinAnimator.mSurface
+                    Slog.i(TAG, "  mSurface=" + w.mWinAnimator.mSurfaceControl
                             + " relayoutCalled=" + w.mRelayoutCalled + " viewVis=" + w.mViewVisibility
                             + " policyVis=" + w.mPolicyVisibility
                             + " policyVisAfterAnim=" + w.mPolicyVisibilityAfterAnim
@@ -2278,14 +2278,14 @@
             TAG, "Remove " + win + " client="
             + Integer.toHexString(System.identityHashCode(
                 win.mClient.asBinder()))
-            + ", surface=" + win.mWinAnimator.mSurface);
+            + ", surface=" + win.mWinAnimator.mSurfaceControl);
 
         final long origId = Binder.clearCallingIdentity();
 
         win.disposeInputChannel();
 
         if (DEBUG_APP_TRANSITIONS) Slog.v(
-                TAG, "Remove " + win + ": mSurface=" + win.mWinAnimator.mSurface
+                TAG, "Remove " + win + ": mSurface=" + win.mWinAnimator.mSurfaceControl
                 + " mExiting=" + win.mExiting
                 + " isAnimating=" + win.mWinAnimator.isAnimating()
                 + " app-animation="
@@ -2775,9 +2775,9 @@
                     if (!win.mHasSurface) {
                         surfaceChanged = true;
                     }
-                    SurfaceControl surface = winAnimator.createSurfaceLocked();
-                    if (surface != null) {
-                        outSurface.copyFrom(surface);
+                    SurfaceControl surfaceControl = winAnimator.createSurfaceLocked();
+                    if (surfaceControl != null) {
+                        outSurface.copyFrom(surfaceControl);
                         if (SHOW_TRANSACTIONS) Slog.i(TAG,
                                 "  OUT SURFACE " + outSurface + ": copied");
                     } else {
@@ -2817,7 +2817,7 @@
                 }
             } else {
                 winAnimator.mEnterAnimationPending = false;
-                if (winAnimator.mSurface != null) {
+                if (winAnimator.mSurfaceControl != null) {
                     if (DEBUG_VISIBILITY) Slog.i(TAG, "Relayout invis " + win
                             + ": mExiting=" + win.mExiting);
                     // If we are not currently running the exit animation, we
@@ -7935,15 +7935,15 @@
                     // TODO(multi-display): support other displays
                     final DisplayContent displayContent = getDefaultDisplayContentLocked();
                     final Display display = displayContent.getDisplay();
-                    SurfaceControl surface = new SurfaceControl(mFxSession,
+                    SurfaceControl surfaceControl = new SurfaceControl(mFxSession,
                             "thumbnail anim",
                             dirty.width(), dirty.height(),
                             PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
-                    surface.setLayerStack(display.getLayerStack());
-                    appAnimator.thumbnail = surface;
-                    if (SHOW_TRANSACTIONS) Slog.i(TAG, "  THUMBNAIL " + surface + ": CREATE");
+                    surfaceControl.setLayerStack(display.getLayerStack());
+                    appAnimator.thumbnail = surfaceControl;
+                    if (SHOW_TRANSACTIONS) Slog.i(TAG, "  THUMBNAIL " + surfaceControl + ": CREATE");
                     Surface drawSurface = new Surface();
-                    drawSurface.copyFrom(surface);
+                    drawSurface.copyFrom(surfaceControl);
                     Canvas c = drawSurface.lockCanvas(dirty);
                     c.drawBitmap(nextAppTransitionThumbnail, 0, 0, null);
                     drawSurface.unlockCanvasAndPost(c);
@@ -8064,7 +8064,7 @@
                 if (w.mOrientationChanging) {
                     if (DEBUG_SURFACE_TRACE || DEBUG_ANIM || DEBUG_ORIENTATION) Slog.v(TAG,
                             "Orientation start waiting for draw mDrawState=DRAW_PENDING in "
-                            + w + ", surface " + winAnimator.mSurface);
+                            + w + ", surface " + winAnimator.mSurfaceControl);
                     winAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
                     if (w.mAppToken != null) {
                         w.mAppToken.allDrawn = false;
@@ -8081,7 +8081,7 @@
                 if (w.isDrawnLw()) {
                     if (DEBUG_ORIENTATION) Slog.v(TAG,
                             "Orientation not waiting for draw in "
-                            + w + ", surface " + winAnimator.mSurface);
+                            + w + ", surface " + winAnimator.mSurfaceControl);
                     w.mOrientationChanging = false;
                 }
             }
@@ -8429,7 +8429,7 @@
                                     Slog.v(TAG, "Eval win " + w + ": isDrawn=" + w.isDrawnLw()
                                             + ", isAnimating=" + winAnimator.isAnimating());
                                     if (!w.isDrawnLw()) {
-                                        Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurface
+                                        Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurfaceControl
                                                 + " pv=" + w.mPolicyVisibility
                                                 + " mDrawState=" + winAnimator.mDrawState
                                                 + " ah=" + w.mAttachedHidden
@@ -8914,7 +8914,7 @@
 
     boolean reclaimSomeSurfaceMemoryLocked(WindowStateAnimator winAnimator, String operation,
                                            boolean secure) {
-        final SurfaceControl surface = winAnimator.mSurface;
+        final SurfaceControl surface = winAnimator.mSurfaceControl;
         boolean leakedSurface = false;
         boolean killedApps = false;
 
@@ -8936,28 +8936,28 @@
             while (mTmpWindowsIterator.hasNext()) {
                 WindowState ws = mTmpWindowsIterator.next();
                 WindowStateAnimator wsa = ws.mWinAnimator;
-                if (wsa.mSurface != null) {
+                if (wsa.mSurfaceControl != null) {
                     if (!mSessions.contains(wsa.mSession)) {
                         Slog.w(TAG, "LEAKED SURFACE (session doesn't exist): "
-                                + ws + " surface=" + wsa.mSurface
+                                + ws + " surface=" + wsa.mSurfaceControl
                                 + " token=" + ws.mToken
                                 + " pid=" + ws.mSession.mPid
                                 + " uid=" + ws.mSession.mUid);
                         if (SHOW_TRANSACTIONS) logSurface(ws, "LEAK DESTROY", null);
-                        wsa.mSurface.destroy();
+                        wsa.mSurfaceControl.destroy();
                         wsa.mSurfaceShown = false;
-                        wsa.mSurface = null;
+                        wsa.mSurfaceControl = null;
                         ws.mHasSurface = false;
                         mForceRemoves.add(ws);
                         leakedSurface = true;
                     } else if (ws.mAppToken != null && ws.mAppToken.clientHidden) {
                         Slog.w(TAG, "LEAKED SURFACE (app token hidden): "
-                                + ws + " surface=" + wsa.mSurface
+                                + ws + " surface=" + wsa.mSurfaceControl
                                 + " token=" + ws.mAppToken);
                         if (SHOW_TRANSACTIONS) logSurface(ws, "LEAK DESTROY", null);
-                        wsa.mSurface.destroy();
+                        wsa.mSurfaceControl.destroy();
                         wsa.mSurfaceShown = false;
-                        wsa.mSurface = null;
+                        wsa.mSurfaceControl = null;
                         ws.mHasSurface = false;
                         leakedSurface = true;
                     }
@@ -8974,7 +8974,7 @@
                         continue;
                     }
                     WindowStateAnimator wsa = ws.mWinAnimator;
-                    if (wsa.mSurface != null) {
+                    if (wsa.mSurfaceControl != null) {
                         pidCandidates.append(wsa.mSession.mPid, wsa.mSession.mPid);
                     }
                 }
@@ -9001,7 +9001,7 @@
                             "RECOVER DESTROY", null);
                     surface.destroy();
                     winAnimator.mSurfaceShown = false;
-                    winAnimator.mSurface = null;
+                    winAnimator.mSurfaceControl = null;
                     winAnimator.mWin.mHasSurface = false;
                 }
 
diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java
index 82e59a8..3a9f7cb 100644
--- a/services/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/java/com/android/server/wm/WindowStateAnimator.java
@@ -88,7 +88,7 @@
     int mAnimLayer;
     int mLastLayer;
 
-    SurfaceControl mSurface;
+    SurfaceControl mSurfaceControl;
     SurfaceControl mPendingDestroySurface;
 
     /**
@@ -404,7 +404,7 @@
         if (WindowManagerService.localLOGV) Slog.v(
                 TAG, "Exit animation finished in " + this
                 + ": remove=" + mWin.mRemoveOnExit);
-        if (mSurface != null) {
+        if (mSurfaceControl != null) {
             mService.mDestroySurface.add(mWin);
             mWin.mDestroying = true;
             if (WindowState.SHOW_TRANSACTIONS) WindowManagerService.logSurface(
@@ -425,10 +425,10 @@
             mLastHidden = true;
             if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin,
                     "HIDE (performLayout)", null);
-            if (mSurface != null) {
+            if (mSurfaceControl != null) {
                 mSurfaceShown = false;
                 try {
-                    mSurface.hide();
+                    mSurfaceControl.hide();
                 } catch (RuntimeException e) {
                     Slog.w(TAG, "Exception hiding surface in " + mWin);
                 }
@@ -445,7 +445,7 @@
         if (mDrawState == DRAW_PENDING) {
             if (DEBUG_SURFACE_TRACE || DEBUG_ANIM || SHOW_TRANSACTIONS || DEBUG_ORIENTATION)
                 Slog.v(TAG, "finishDrawingLocked: mDrawState=COMMIT_DRAW_PENDING " + this + " in "
-                        + mSurface);
+                        + mSurfaceControl);
             if (DEBUG_STARTING_WINDOW &&
                     mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING) {
                 Slog.v(TAG, "Draw state now committed in " + mWin);
@@ -467,7 +467,7 @@
             return false;
         }
         if (DEBUG_SURFACE_TRACE || DEBUG_ANIM) {
-            Slog.i(TAG, "commitFinishDrawingLocked: mDrawState=READY_TO_SHOW " + mSurface);
+            Slog.i(TAG, "commitFinishDrawingLocked: mDrawState=READY_TO_SHOW " + mSurfaceControl);
         }
         mDrawState = READY_TO_SHOW;
         final boolean starting = mWin.mAttrs.type == TYPE_APPLICATION_STARTING;
@@ -625,7 +625,7 @@
     }
 
     SurfaceControl createSurfaceLocked() {
-        if (mSurface == null) {
+        if (mSurfaceControl == null) {
             if (DEBUG_ANIM || DEBUG_ORIENTATION) Slog.i(TAG,
                     "createSurface " + this + ": mDrawState=DRAW_PENDING");
             mDrawState = DRAW_PENDING;
@@ -685,12 +685,12 @@
                     flags |= SurfaceControl.OPAQUE;
                 }
                 if (DEBUG_SURFACE_TRACE) {
-                    mSurface = new SurfaceTrace(
+                    mSurfaceControl = new SurfaceTrace(
                             mSession.mSurfaceSession,
                             attrs.getTitle().toString(),
                             w, h, format, flags);
                 } else {
-                    mSurface = new SurfaceControl(
+                    mSurfaceControl = new SurfaceControl(
                         mSession.mSurfaceSession,
                         attrs.getTitle().toString(),
                         w, h, format, flags);
@@ -698,7 +698,7 @@
                 mWin.mHasSurface = true;
                 if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG,
                         "  CREATE SURFACE "
-                        + mSurface + " IN SESSION "
+                        + mSurfaceControl + " IN SESSION "
                         + mSession.mSurfaceSession
                         + ": pid=" + mSession.mPid + " format="
                         + attrs.format + " flags=0x"
@@ -718,7 +718,7 @@
             }
 
             if (WindowManagerService.localLOGV) Slog.v(
-                TAG, "Got surface: " + mSurface
+                TAG, "Got surface: " + mSurfaceControl
                 + ", set left=" + mWin.mFrame.left + " top=" + mWin.mFrame.top
                 + ", animLayer=" + mAnimLayer);
             if (SHOW_LIGHT_TRANSACTIONS) {
@@ -733,11 +733,11 @@
                 try {
                     mSurfaceX = mWin.mFrame.left + mWin.mXOffset;
                     mSurfaceY = mWin.mFrame.top + mWin.mYOffset;
-                    mSurface.setPosition(mSurfaceX, mSurfaceY);
+                    mSurfaceControl.setPosition(mSurfaceX, mSurfaceY);
                     mSurfaceLayer = mAnimLayer;
-                    mSurface.setLayerStack(mLayerStack);
-                    mSurface.setLayer(mAnimLayer);
-                    mSurface.setAlpha(0);
+                    mSurfaceControl.setLayerStack(mLayerStack);
+                    mSurfaceControl.setLayer(mAnimLayer);
+                    mSurfaceControl.setAlpha(0);
                     mSurfaceShown = false;
                 } catch (RuntimeException e) {
                     Slog.w(TAG, "Error creating surface in " + w, e);
@@ -752,7 +752,7 @@
             if (WindowManagerService.localLOGV) Slog.v(
                     TAG, "Created surface " + this);
         }
-        return mSurface;
+        return mSurfaceControl;
     }
 
     void destroySurfaceLocked() {
@@ -760,7 +760,7 @@
             mWin.mAppToken.startingDisplayed = false;
         }
 
-        if (mSurface != null) {
+        if (mSurfaceControl != null) {
 
             int i = mWin.mChildWindows.size();
             while (i > 0) {
@@ -777,10 +777,10 @@
                         e.fillInStackTrace();
                     }
                     Slog.w(TAG, "Window " + this + " destroying surface "
-                            + mSurface + ", session " + mSession, e);
+                            + mSurfaceControl + ", session " + mSession, e);
                 }
                 if (mSurfaceDestroyDeferred) {
-                    if (mSurface != null && mPendingDestroySurface != mSurface) {
+                    if (mSurfaceControl != null && mPendingDestroySurface != mSurfaceControl) {
                         if (mPendingDestroySurface != null) {
                             if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
                                 RuntimeException e = null;
@@ -792,7 +792,7 @@
                             }
                             mPendingDestroySurface.destroy();
                         }
-                        mPendingDestroySurface = mSurface;
+                        mPendingDestroySurface = mSurfaceControl;
                     }
                 } else {
                     if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
@@ -803,17 +803,17 @@
                         }
                         WindowManagerService.logSurface(mWin, "DESTROY", e);
                     }
-                    mSurface.destroy();
+                    mSurfaceControl.destroy();
                 }
                 mAnimator.hideWallpapersLocked(mWin);
             } catch (RuntimeException e) {
                 Slog.w(TAG, "Exception thrown when destroying Window " + this
-                    + " surface " + mSurface + " session " + mSession
+                    + " surface " + mSurfaceControl + " session " + mSession
                     + ": " + e.toString());
             }
 
             mSurfaceShown = false;
-            mSurface = null;
+            mSurfaceControl = null;
             mWin.mHasSurface = false;
             mDrawState = NO_SURFACE;
         }
@@ -1120,7 +1120,7 @@
             try {
                 if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
                         "CROP " + w.mSystemDecorRect.toShortString(), null);
-                mSurface.setWindowCrop(w.mSystemDecorRect);
+                mSurfaceControl.setWindowCrop(w.mSystemDecorRect);
             } catch (RuntimeException e) {
                 Slog.w(TAG, "Error setting crop surface of " + w
                         + " crop=" + w.mSystemDecorRect.toShortString(), e);
@@ -1164,7 +1164,7 @@
                         "POS " + left + ", " + top, null);
                 mSurfaceX = left;
                 mSurfaceY = top;
-                mSurface.setPosition(left, top);
+                mSurfaceControl.setPosition(left, top);
             } catch (RuntimeException e) {
                 Slog.w(TAG, "Error positioning surface of " + w
                         + " pos=(" + left
@@ -1180,7 +1180,7 @@
                 if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
                         "SIZE " + width + "x" + height, null);
                 mSurfaceResized = true;
-                mSurface.setSize(width, height);
+                mSurfaceControl.setSize(width, height);
                 final int displayId = w.mDisplayContent.getDisplayId();
                 mAnimator.setPendingLayoutChanges(displayId,
                         WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
@@ -1204,7 +1204,7 @@
 
     public void prepareSurfaceLocked(final boolean recoveringMemory) {
         final WindowState w = mWin;
-        if (mSurface == null) {
+        if (mSurfaceControl == null) {
             if (w.mOrientationChanging) {
                 if (DEBUG_ORIENTATION) {
                     Slog.v(TAG, "Orientation change skips hidden " + w);
@@ -1262,13 +1262,13 @@
                     + "," + (mDtDx*w.mVScale)
                     + "][" + (mDsDy*w.mHScale)
                     + "," + (mDtDy*w.mVScale) + "]", null);
-            if (mSurface != null) {
+            if (mSurfaceControl != null) {
                 try {
                     mSurfaceAlpha = mShownAlpha;
-                    mSurface.setAlpha(mShownAlpha);
+                    mSurfaceControl.setAlpha(mShownAlpha);
                     mSurfaceLayer = mAnimLayer;
-                    mSurface.setLayer(mAnimLayer);
-                    mSurface.setMatrix(
+                    mSurfaceControl.setLayer(mAnimLayer);
+                    mSurfaceControl.setMatrix(
                         mDsDx*w.mHScale, mDtDx*w.mVScale,
                         mDsDy*w.mHScale, mDtDy*w.mVScale);
 
@@ -1286,7 +1286,7 @@
                             w.mOrientationChanging = false;
                         }
                     }
-                    if (mSurface != null) {
+                    if (mSurfaceControl != null) {
                         w.mToken.hasVisible = true;
                     }
                 } catch (RuntimeException e) {
@@ -1319,7 +1319,7 @@
     }
 
     void setTransparentRegionHintLocked(final Region region) {
-        if (mSurface == null) {
+        if (mSurfaceControl == null) {
             Slog.w(TAG, "setTransparentRegionHint: null mSurface after mHasSurface true");
             return;
         }
@@ -1329,7 +1329,7 @@
         try {
             if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin,
                     "transparentRegionHint=" + region, null);
-            mSurface.setTransparentRegionHint(region);
+            mSurfaceControl.setTransparentRegionHint(region);
         } finally {
             SurfaceControl.closeTransaction();
             if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
@@ -1356,7 +1356,7 @@
             try {
                 if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin,
                         "POS " + left + ", " + top, null);
-                mSurface.setPosition(mWin.mFrame.left + left, mWin.mFrame.top + top);
+                mSurfaceControl.setPosition(mWin.mFrame.left + left, mWin.mFrame.top + top);
                 updateSurfaceWindowCrop(false);
             } catch (RuntimeException e) {
                 Slog.w(TAG, "Error positioning surface of " + mWin
@@ -1431,7 +1431,7 @@
                 WindowState c = mWin.mChildWindows.get(i);
                 if (c.mAttachedHidden) {
                     c.mAttachedHidden = false;
-                    if (c.mWinAnimator.mSurface != null) {
+                    if (c.mWinAnimator.mSurfaceControl != null) {
                         c.mWinAnimator.performShowLocked();
                         // It hadn't been shown, which means layout not
                         // performed on it, so now we want to make sure to
@@ -1479,9 +1479,9 @@
      */
     boolean showSurfaceRobustlyLocked() {
         try {
-            if (mSurface != null) {
+            if (mSurfaceControl != null) {
                 mSurfaceShown = true;
-                mSurface.show();
+                mSurfaceControl.show();
                 if (mWin.mTurnOnScreen) {
                     if (DEBUG_VISIBILITY) Slog.v(TAG,
                             "Show surface turning screen on: " + mWin);
@@ -1491,7 +1491,7 @@
             }
             return true;
         } catch (RuntimeException e) {
-            Slog.w(TAG, "Failure showing surface " + mSurface + " in " + mWin, e);
+            Slog.w(TAG, "Failure showing surface " + mSurfaceControl + " in " + mWin, e);
         }
 
         mService.reclaimSomeSurfaceMemoryLocked(this, "show", true);
@@ -1600,9 +1600,9 @@
                     pw.print(" "); mTransformation.printShortString(pw);
                     pw.println();
         }
-        if (mSurface != null) {
+        if (mSurfaceControl != null) {
             if (dumpAll) {
-                pw.print(prefix); pw.print("mSurface="); pw.println(mSurface);
+                pw.print(prefix); pw.print("mSurface="); pw.println(mSurfaceControl);
                 pw.print(prefix); pw.print("mDrawState=");
                 pw.print(drawStateToString(mDrawState));
                 pw.print(" mLastHidden="); pw.println(mLastHidden);
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java
index fb818d4..9939c08 100644
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java
+++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java
@@ -41,13 +41,40 @@
     protected int mask() { return 0x0; };
 
     private static final RectF gRect = new RectF(0, 0, 200, 175);
-    private static final float[] gLinePts = new float[] {
-            100, 0, 200, 200, 200, 200, 0, 200, 0, 200, 100, 0
-    };
     private static final float[] gPts = new float[] {
             0, 100, 100, 0, 100, 200, 200, 100
     };
 
+    private static final int NUM_PARALLEL_LINES = 24;
+    private static final float[] gTriPts = new float[] {
+        75, 0, 130, 130, 130, 130, 0, 130, 0, 130, 75, 0
+    };
+    private static final float[] gLinePts = new float[NUM_PARALLEL_LINES * 8 + gTriPts.length];
+    static {
+        int index;
+        for (index = 0; index < gTriPts.length; index++) {
+            gLinePts[index] = gTriPts[index];
+        }
+        float val = 0;
+        for (int i = 0; i < NUM_PARALLEL_LINES; i++) {
+            gLinePts[index + 0] = 150;
+            gLinePts[index + 1] = val;
+            gLinePts[index + 2] = 300;
+            gLinePts[index + 3] = val;
+            index += 4;
+            val += 8 + (2.0f/NUM_PARALLEL_LINES);
+        }
+        val = 0;
+        for (int i = 0; i < NUM_PARALLEL_LINES; i++) {
+            gLinePts[index + 0] = val;
+            gLinePts[index + 1] = 150;
+            gLinePts[index + 2] = val;
+            gLinePts[index + 3] = 300;
+            index += 4;
+            val += 8 + (2.0f/NUM_PARALLEL_LINES);
+        }
+    };
+
     @SuppressWarnings("serial")
     private static final LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>> gMaps = new LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>>() {
         {
@@ -307,7 +334,7 @@
                             canvas.drawOval(gRect, paint);
                         }
                     });
-                    put("triLines", new DisplayModifier() {
+                    put("lines", new DisplayModifier() {
                         @Override
                         public void modifyDrawing(Paint paint, Canvas canvas) {
                             canvas.drawLines(gLinePts, paint);
diff --git a/tests/RenderScriptTests/LivePreview/src/com/android/rs/livepreview/RsYuv.java b/tests/RenderScriptTests/LivePreview/src/com/android/rs/livepreview/RsYuv.java
index 4d1627d..12d3185 100644
--- a/tests/RenderScriptTests/LivePreview/src/com/android/rs/livepreview/RsYuv.java
+++ b/tests/RenderScriptTests/LivePreview/src/com/android/rs/livepreview/RsYuv.java
@@ -80,8 +80,13 @@
         Type t = tb.create();
         mAllocationOut = Allocation.createTyped(mRS, t, Allocation.USAGE_SCRIPT |
                                                         Allocation.USAGE_IO_OUTPUT);
-        mAllocationIn = Allocation.createSized(mRS, Element.U8(mRS), (mHeight * mWidth) +
-                                               ((mHeight / 2) * (mWidth / 2) * 2));
+
+
+        tb = new Type.Builder(mRS, Element.createPixel(mRS, Element.DataType.UNSIGNED_8, Element.DataKind.PIXEL_YUV));
+        tb.setX(mWidth);
+        tb.setY(mHeight);
+        tb.setYuvFormat(android.graphics.ImageFormat.NV21);
+        mAllocationIn = Allocation.createTyped(mRS, tb.create(), Allocation.USAGE_SCRIPT);
         mYuv.setInput(mAllocationIn);
         setupSurface();