Merge "Revert "Use ObjectAnimator for fading scrollbars, set initial duration to 1500""
diff --git a/api/current.txt b/api/current.txt
index 285d75d..e92e9f4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1719,6 +1719,7 @@
     field public static final int pasteAsPlainText = 16908337; // 0x1020031
     field public static final int primary = 16908300; // 0x102000c
     field public static final int progress = 16908301; // 0x102000d
+    field public static final int redo = 16908339; // 0x1020033
     field public static final int secondaryProgress = 16908303; // 0x102000f
     field public static final int selectAll = 16908319; // 0x102001f
     field public static final int selectTextMode = 16908333; // 0x102002d
@@ -1735,6 +1736,7 @@
     field public static final int text2 = 16908309; // 0x1020015
     field public static final int title = 16908310; // 0x1020016
     field public static final int toggle = 16908311; // 0x1020017
+    field public static final int undo = 16908338; // 0x1020032
     field public static final int widget_frame = 16908312; // 0x1020018
   }
 
diff --git a/api/system-current.txt b/api/system-current.txt
index 0393896..29ebf57 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1795,6 +1795,7 @@
     field public static final int pasteAsPlainText = 16908337; // 0x1020031
     field public static final int primary = 16908300; // 0x102000c
     field public static final int progress = 16908301; // 0x102000d
+    field public static final int redo = 16908339; // 0x1020033
     field public static final int secondaryProgress = 16908303; // 0x102000f
     field public static final int selectAll = 16908319; // 0x102001f
     field public static final int selectTextMode = 16908333; // 0x102002d
@@ -1811,6 +1812,7 @@
     field public static final int text2 = 16908309; // 0x1020015
     field public static final int title = 16908310; // 0x1020016
     field public static final int toggle = 16908311; // 0x1020017
+    field public static final int undo = 16908338; // 0x1020032
     field public static final int widget_frame = 16908312; // 0x1020018
   }
 
diff --git a/core/java/android/content/UndoManager.java b/core/java/android/content/UndoManager.java
index e3bc238..559b01c 100644
--- a/core/java/android/content/UndoManager.java
+++ b/core/java/android/content/UndoManager.java
@@ -105,8 +105,7 @@
             return owner;
         }
 
-        owner = new UndoOwner(tag);
-        owner.mManager = this;
+        owner = new UndoOwner(tag, this);
         owner.mData = data;
         mOwners.put(tag, owner);
         return owner;
@@ -116,7 +115,6 @@
         // XXX need to figure out how to prune.
         if (false) {
             mOwners.remove(owner.mTag);
-            owner.mManager = null;
         }
     }
 
@@ -202,7 +200,7 @@
         UndoOwner owner = mStateOwners[idx];
         if (owner == null) {
             String tag = in.readString();
-            owner = new UndoOwner(tag);
+            owner = new UndoOwner(tag, this);
             mStateOwners[idx] = owner;
             mOwners.put(tag, owner);
         }
diff --git a/core/java/android/content/UndoOwner.java b/core/java/android/content/UndoOwner.java
index d0cdc95..9106588 100644
--- a/core/java/android/content/UndoOwner.java
+++ b/core/java/android/content/UndoOwner.java
@@ -23,8 +23,8 @@
  */
 public class UndoOwner {
     final String mTag;
+    final UndoManager mManager;
 
-    UndoManager mManager;
     Object mData;
     int mOpCount;
 
@@ -32,8 +32,15 @@
     int mStateSeq;
     int mSavedIdx;
 
-    UndoOwner(String tag) {
+    UndoOwner(String tag, UndoManager manager) {
+        if (tag == null) {
+            throw new NullPointerException("tag can't be null");
+        }
+        if (manager == null) {
+            throw new NullPointerException("manager can't be null");
+        }
         mTag = tag;
+        mManager = manager;
     }
 
     /**
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 4752594..8601d2b 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -50,6 +50,7 @@
 import android.inputmethodservice.ExtractEditText;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.ParcelableParcel;
 import android.os.SystemClock;
 import android.provider.Settings;
 import android.text.DynamicLayout;
@@ -118,15 +119,18 @@
  */
 public class Editor {
     private static final String TAG = "Editor";
-    static final boolean DEBUG_UNDO = false;
+    private static final boolean DEBUG_UNDO = false;
 
     static final int BLINK = 500;
     private static final float[] TEMP_POSITION = new float[2];
     private static int DRAG_SHADOW_MAX_TEXT_LENGTH = 20;
+    // Tag used when the Editor maintains its own separate UndoManager.
+    private static final String UNDO_OWNER_TAG = "Editor";
 
-    UndoManager mUndoManager;
-    UndoOwner mUndoOwner;
-    InputFilter mUndoInputFilter;
+    // Each Editor manages its own undo stack.
+    private final UndoManager mUndoManager = new UndoManager();
+    private UndoOwner mUndoOwner = mUndoManager.getOwner(UNDO_OWNER_TAG, this);
+    final InputFilter mUndoInputFilter = new UndoInputFilter(this);
 
     // Cursor Controllers.
     InsertionPointCursorController mInsertionPointCursorController;
@@ -222,6 +226,39 @@
 
     Editor(TextView textView) {
         mTextView = textView;
+        // Synchronize the filter list, which places the undo input filter at the end.
+        mTextView.setFilters(mTextView.getFilters());
+    }
+
+    ParcelableParcel saveInstanceState() {
+        // For now there is only undo state.
+        return (ParcelableParcel) mUndoManager.saveInstanceState();
+    }
+
+    void restoreInstanceState(ParcelableParcel state) {
+        mUndoManager.restoreInstanceState(state);
+        // Re-associate this object as the owner of undo state.
+        mUndoOwner = mUndoManager.getOwner(UNDO_OWNER_TAG, this);
+    }
+
+    boolean canUndo() {
+        UndoOwner[] owners = { mUndoOwner };
+        return mUndoManager.countUndos(owners) > 0;
+    }
+
+    boolean canRedo() {
+        UndoOwner[] owners = { mUndoOwner };
+        return mUndoManager.countRedos(owners) > 0;
+    }
+
+    void undo() {
+        UndoOwner[] owners = { mUndoOwner };
+        mUndoManager.undo(owners, 1);  // Undo 1 action.
+    }
+
+    void redo() {
+        UndoOwner[] owners = { mUndoOwner };
+        mUndoManager.redo(owners, 1);  // Redo 1 action.
     }
 
     void onAttachedToWindow() {
@@ -1706,7 +1743,7 @@
 
     /**
      * Called by the framework in response to a text auto-correction (such as fixing a typo using a
-     * a dictionnary) from the current input method, provided by it calling
+     * a dictionary) from the current input method, provided by it calling
      * {@link InputConnection#commitCorrection} InputConnection.commitCorrection()}. The default
      * implementation flashes the background of the corrected word to provide feedback to the user.
      *
@@ -4161,8 +4198,12 @@
         int mChangedStart, mChangedEnd, mChangedDelta;
     }
 
+    /**
+     * An InputFilter that monitors text input to maintain undo history. It does not modify the
+     * text being typed (and hence always returns null from the filter() method).
+     */
     public static class UndoInputFilter implements InputFilter {
-        final Editor mEditor;
+        private final Editor mEditor;
 
         public UndoInputFilter(Editor editor) {
             mEditor = editor;
@@ -4192,6 +4233,8 @@
                     // The current operation is an add...  are we adding more?  We are adding
                     // more if we are either appending new text to the end of the last edit or
                     // completely replacing some or all of the last edit.
+                    // TODO: This sequence doesn't work right: a, left-arrow, b, undo, undo.
+                    // The two edits are incorrectly merged, so there is only one undo available.
                     if (start < end && ((dstart >= op.mRangeStart && dend <= op.mRangeEnd)
                             || (dstart == op.mRangeEnd && dend == op.mRangeEnd))) {
                         op.mRangeEnd = dstart + (end-start);
@@ -4245,7 +4288,10 @@
         }
     }
 
-    public static class TextModifyOperation extends UndoOperation<TextView> {
+    /**
+     * An operation to undo a single "edit" to a text view.
+     */
+    public static class TextModifyOperation extends UndoOperation<Editor> {
         int mRangeStart, mRangeEnd;
         CharSequence mOldText;
 
@@ -4277,8 +4323,8 @@
         private void swapText() {
             // Both undo and redo involves swapping the contents of the range
             // in the text view with our local text.
-            TextView tv = getOwnerData();
-            Editable editable = (Editable)tv.getText();
+            Editor editor = getOwnerData();
+            Editable editable = (Editable)editor.mTextView.getText();
             CharSequence curText;
             if (mRangeStart >= mRangeEnd) {
                 curText = null;
@@ -4309,14 +4355,17 @@
 
         public static final Parcelable.ClassLoaderCreator<TextModifyOperation> CREATOR
                 = new Parcelable.ClassLoaderCreator<TextModifyOperation>() {
+            @Override
             public TextModifyOperation createFromParcel(Parcel in) {
                 return new TextModifyOperation(in, null);
             }
 
+            @Override
             public TextModifyOperation createFromParcel(Parcel in, ClassLoader loader) {
                 return new TextModifyOperation(in, loader);
             }
 
+            @Override
             public TextModifyOperation[] newArray(int size) {
                 return new TextModifyOperation[size];
             }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 27603f5..2d0a9cb 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -47,6 +47,7 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.ParcelableParcel;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -1612,7 +1613,8 @@
      * @hide
      */
     public final UndoManager getUndoManager() {
-        return mEditor == null ? null : mEditor.mUndoManager;
+        // TODO: Consider supporting a global undo manager.
+        throw new UnsupportedOperationException("not implemented");
     }
 
     /**
@@ -1630,22 +1632,12 @@
      * @hide
      */
     public final void setUndoManager(UndoManager undoManager, String tag) {
-        if (undoManager != null) {
-            createEditorIfNeeded();
-            mEditor.mUndoManager = undoManager;
-            mEditor.mUndoOwner = undoManager.getOwner(tag, this);
-            mEditor.mUndoInputFilter = new Editor.UndoInputFilter(mEditor);
-            if (!(mText instanceof Editable)) {
-                setText(mText, BufferType.EDITABLE);
-            }
-
-            setFilters((Editable) mText, mFilters);
-        } else if (mEditor != null) {
-            // XXX need to destroy all associated state.
-            mEditor.mUndoManager = null;
-            mEditor.mUndoOwner = null;
-            mEditor.mUndoInputFilter = null;
-        }
+        // TODO: Consider supporting a global undo manager. An implementation will need to:
+        // * createEditorIfNeeded()
+        // * Promote to BufferType.EDITABLE if needed.
+        // * Update the UndoManager and UndoOwner.
+        // Likewise it will need to be able to restore the default UndoManager.
+        throw new UnsupportedOperationException("not implemented");
     }
 
     /**
@@ -3899,6 +3891,9 @@
 
             ss.error = getError();
 
+            if (mEditor != null) {
+                ss.editorState = mEditor.saveInstanceState();
+            }
             return ss;
         }
 
@@ -3968,6 +3963,11 @@
                 }
             });
         }
+
+        if (ss.editorState != null) {
+            createEditorIfNeeded();
+            mEditor.restoreInstanceState(ss.editorState);
+        }
     }
 
     /**
@@ -8383,6 +8383,11 @@
                     return onTextContextMenuItem(ID_SELECT_ALL);
                 }
                 break;
+            case KeyEvent.KEYCODE_Z:
+                if (canUndo()) {
+                    return onTextContextMenuItem(ID_UNDO);
+                }
+                break;
             case KeyEvent.KEYCODE_X:
                 if (canCut()) {
                     return onTextContextMenuItem(ID_CUT);
@@ -8402,11 +8407,15 @@
         } else if (event.hasModifiers(KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON)) {
             // Handle Ctrl-Shift shortcuts.
             switch (keyCode) {
+                case KeyEvent.KEYCODE_Z:
+                    if (canRedo()) {
+                        return onTextContextMenuItem(ID_REDO);
+                    }
+                    break;
                 case KeyEvent.KEYCODE_V:
                     if (canPaste()) {
                         return onTextContextMenuItem(ID_PASTE_AS_PLAIN_TEXT);
                     }
-                    break;
             }
         }
         return super.onKeyShortcut(keyCode, event);
@@ -8784,6 +8793,8 @@
     }
 
     static final int ID_SELECT_ALL = android.R.id.selectAll;
+    static final int ID_UNDO = android.R.id.undo;
+    static final int ID_REDO = android.R.id.redo;
     static final int ID_CUT = android.R.id.cut;
     static final int ID_COPY = android.R.id.copy;
     static final int ID_PASTE = android.R.id.paste;
@@ -8815,6 +8826,18 @@
                 selectAllText();
                 return true;
 
+            case ID_UNDO:
+                if (mEditor != null) {
+                    mEditor.undo();
+                }
+                return true;  // Returns true even if nothing was undone.
+
+            case ID_REDO:
+                if (mEditor != null) {
+                    mEditor.redo();
+                }
+                return true;  // Returns true even if nothing was undone.
+
             case ID_PASTE:
                 paste(min, max, true /* withFormatting */);
                 return true;
@@ -8948,7 +8971,17 @@
      * @hide
      */
     protected void stopSelectionActionMode() {
-        mEditor.stopSelectionActionMode();
+        if (mEditor != null) {
+            mEditor.stopSelectionActionMode();
+        }
+    }
+
+    boolean canUndo() {
+        return mEditor != null && mEditor.canUndo();
+    }
+
+    boolean canRedo() {
+        return mEditor != null && mEditor.canRedo();
     }
 
     boolean canCut() {
@@ -9325,6 +9358,7 @@
         CharSequence text;
         boolean frozenWithFocus;
         CharSequence error;
+        ParcelableParcel editorState;  // Optional state from Editor.
 
         SavedState(Parcelable superState) {
             super(superState);
@@ -9344,6 +9378,13 @@
                 out.writeInt(1);
                 TextUtils.writeToParcel(error, out, flags);
             }
+
+            if (editorState == null) {
+                out.writeInt(0);
+            } else {
+                out.writeInt(1);
+                editorState.writeToParcel(out, flags);
+            }
         }
 
         @Override
@@ -9379,6 +9420,10 @@
             if (in.readInt() != 0) {
                 error = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
             }
+
+            if (in.readInt() != 0) {
+                editorState = ParcelableParcel.CREATOR.createFromParcel(in);
+            }
         }
     }
 
diff --git a/core/java/android/alsa/AlsaCardsParser.java b/core/java/com/android/internal/alsa/AlsaCardsParser.java
similarity index 99%
rename from core/java/android/alsa/AlsaCardsParser.java
rename to core/java/com/android/internal/alsa/AlsaCardsParser.java
index 5e88bca..5c0a888 100644
--- a/core/java/android/alsa/AlsaCardsParser.java
+++ b/core/java/com/android/internal/alsa/AlsaCardsParser.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.alsa;
+package com.android.internal.alsa;
 
 import android.util.Slog;
 import java.io.BufferedReader;
diff --git a/core/java/android/alsa/AlsaDevicesParser.java b/core/java/com/android/internal/alsa/AlsaDevicesParser.java
similarity index 99%
rename from core/java/android/alsa/AlsaDevicesParser.java
rename to core/java/com/android/internal/alsa/AlsaDevicesParser.java
index b140d3d..81b7943 100644
--- a/core/java/android/alsa/AlsaDevicesParser.java
+++ b/core/java/com/android/internal/alsa/AlsaDevicesParser.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.alsa;
+package com.android.internal.alsa;
 
 import android.util.Slog;
 import java.io.BufferedReader;
diff --git a/core/java/android/alsa/LineTokenizer.java b/core/java/com/android/internal/alsa/LineTokenizer.java
similarity index 97%
rename from core/java/android/alsa/LineTokenizer.java
rename to core/java/com/android/internal/alsa/LineTokenizer.java
index 78c91b5..43047a9 100644
--- a/core/java/android/alsa/LineTokenizer.java
+++ b/core/java/com/android/internal/alsa/LineTokenizer.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.alsa;
+package com.android.internal.alsa;
 
 /**
  * @hide
diff --git a/core/jni/android_util_EventLog.cpp b/core/jni/android_util_EventLog.cpp
index 6c95b8a..5cb8b2e 100644
--- a/core/jni/android_util_EventLog.cpp
+++ b/core/jni/android_util_EventLog.cpp
@@ -188,6 +188,10 @@
             break;
         }
 
+        if (log_msg.id() != LOG_ID_EVENTS) {
+            continue;
+        }
+
         int32_t tag = * (int32_t *) log_msg.msg();
 
         int found = 0;
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 6e2f534..d657bad 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -90,4 +90,6 @@
   <item type="id" name="statusBarBackground" />
   <item type="id" name="navigationBarBackground" />
   <item type="id" name="pasteAsPlainText" />
+  <item type="id" name="undo" />
+  <item type="id" name="redo" />
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 2869091..37f8232 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2645,5 +2645,9 @@
   <!-- Context menu ID for the "Paste as plain text" menu item to to copy the current contents
           of the clipboard into the text view without formatting. -->
   <public type="id" name="pasteAsPlainText" />
-
+  <!-- Context menu ID for the "Undo" menu item to undo the last text edit operation. -->
+  <public type="id" name="undo" />
+  <!-- Context menu ID for the "Redo" menu item to redo the last text edit operation. -->
+  <public type="id" name="redo" />
+ 
 </resources>
diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h
index c9a5679..62da6e08 100644
--- a/libs/hwui/Glop.h
+++ b/libs/hwui/Glop.h
@@ -94,8 +94,10 @@
 
         struct TextureData {
             Texture* texture;
+            GLenum target;
             GLenum filter;
             GLenum clamp;
+            Matrix4* textureTransform;
         } texture;
 
         bool colorEnabled;
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index f50dfc8..3108a8d 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -274,7 +274,8 @@
     TRIGGER_STAGE(kFillStage);
     REQUIRE_STAGES(kMeshStage);
 
-    mOutGlop->fill.texture = { &texture, PaintUtils::getFilter(paint), GL_CLAMP_TO_EDGE };
+    mOutGlop->fill.texture = { &texture,
+            GL_TEXTURE_2D, PaintUtils::getFilter(paint), GL_CLAMP_TO_EDGE, nullptr };
 
     if (paint) {
         int color = paint->getColor();
@@ -315,7 +316,7 @@
     TRIGGER_STAGE(kFillStage);
     REQUIRE_STAGES(kMeshStage);
 
-    mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM };
+    mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
 
     setFill(paint.getColor(), alphaScale, PaintUtils::getXfermode(paint.getXfermode()),
             paint.getShader(), paint.getColorFilter());
@@ -329,7 +330,7 @@
     REQUIRE_STAGES(kMeshStage);
 
     //specify invalid filter/clamp, since these are always static for PathTextures
-    mOutGlop->fill.texture = { &texture, GL_INVALID_ENUM, GL_INVALID_ENUM };
+    mOutGlop->fill.texture = { &texture, GL_TEXTURE_2D, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
 
     setFill(paint.getColor(), alphaScale, PaintUtils::getXfermode(paint.getXfermode()),
             paint.getShader(), paint.getColorFilter());
@@ -345,7 +346,7 @@
     REQUIRE_STAGES(kMeshStage);
 
     //specify invalid filter/clamp, since these are always static for ShadowTextures
-    mOutGlop->fill.texture = { &texture, GL_INVALID_ENUM, GL_INVALID_ENUM };
+    mOutGlop->fill.texture = { &texture, GL_TEXTURE_2D, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
 
     const int ALPHA_BITMASK = SK_ColorBLACK;
     const int COLOR_BITMASK = ~ALPHA_BITMASK;
@@ -366,7 +367,7 @@
     TRIGGER_STAGE(kFillStage);
     REQUIRE_STAGES(kMeshStage);
 
-    mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM };
+    mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
     setFill(SK_ColorBLACK, 1.0f, SkXfermode::kSrcOver_Mode, nullptr, nullptr);
     return *this;
 }
@@ -375,7 +376,7 @@
     TRIGGER_STAGE(kFillStage);
     REQUIRE_STAGES(kMeshStage);
 
-    mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM };
+    mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
     setFill(SK_ColorBLACK, 1.0f, SkXfermode::kClear_Mode, nullptr, nullptr);
     return *this;
 }
@@ -385,7 +386,8 @@
     TRIGGER_STAGE(kFillStage);
     REQUIRE_STAGES(kMeshStage);
 
-    mOutGlop->fill.texture = { &texture, GL_LINEAR, GL_CLAMP_TO_EDGE };
+    mOutGlop->fill.texture = { &texture,
+            GL_TEXTURE_2D, GL_LINEAR, GL_CLAMP_TO_EDGE, nullptr };
     mOutGlop->fill.color = { alpha, alpha, alpha, alpha };
 
     setFill(SK_ColorWHITE, alpha, mode, nullptr, colorFilter);
@@ -394,6 +396,21 @@
     return *this;
 }
 
+GlopBuilder& GlopBuilder::setFillTextureLayer(Layer& layer, float alpha) {
+    TRIGGER_STAGE(kFillStage);
+    REQUIRE_STAGES(kMeshStage);
+
+    mOutGlop->fill.texture = { &(layer.getTexture()),
+            layer.getRenderTarget(), GL_LINEAR, GL_CLAMP_TO_EDGE, &layer.getTexTransform() };
+    mOutGlop->fill.color = { alpha, alpha, alpha, alpha };
+
+    setFill(SK_ColorWHITE, alpha, layer.getMode(), nullptr, layer.getColorFilter());
+
+    mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
+    mDescription.hasTextureTransform = true;
+    return *this;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Transform
 ////////////////////////////////////////////////////////////////////////////////
@@ -492,20 +509,27 @@
 
 void verify(const ProgramDescription& description, const Glop& glop) {
     bool hasTexture = glop.fill.texture.texture != nullptr;
-    LOG_ALWAYS_FATAL_IF(description.hasTexture != hasTexture);
+    LOG_ALWAYS_FATAL_IF(description.hasTexture && description.hasExternalTexture);
+    LOG_ALWAYS_FATAL_IF((description.hasTexture || description.hasExternalTexture )!= hasTexture);
     LOG_ALWAYS_FATAL_IF((glop.mesh.vertices.flags & VertexAttribFlags::kTextureCoord) != hasTexture);
 
     if ((glop.mesh.vertices.flags & VertexAttribFlags::kAlpha) && glop.mesh.vertices.bufferObject) {
         LOG_ALWAYS_FATAL("VBO and alpha attributes are not currently compatible");
     }
+
+    if (description.hasTextureTransform != (glop.fill.texture.textureTransform != nullptr)) {
+        LOG_ALWAYS_FATAL("Texture transform incorrectly specified");
+    }
 }
 
 void GlopBuilder::build() {
     REQUIRE_STAGES(kAllStages);
-
-    mDescription.hasTexture = static_cast<int>(mOutGlop->mesh.vertices.flags & VertexAttribFlags::kTextureCoord);
-    mDescription.hasColors = static_cast<int>(mOutGlop->mesh.vertices.flags & VertexAttribFlags::kColor);
-    mDescription.hasVertexAlpha = static_cast<int>(mOutGlop->mesh.vertices.flags & VertexAttribFlags::kAlpha);
+    if (mOutGlop->mesh.vertices.flags & VertexAttribFlags::kTextureCoord) {
+        mDescription.hasTexture = mOutGlop->fill.texture.target == GL_TEXTURE_2D;
+        mDescription.hasExternalTexture = mOutGlop->fill.texture.target == GL_TEXTURE_EXTERNAL_OES;
+    }
+    mDescription.hasColors = mOutGlop->mesh.vertices.flags & VertexAttribFlags::kColor;
+    mDescription.hasVertexAlpha = mOutGlop->mesh.vertices.flags & VertexAttribFlags::kAlpha;
 
     // serialize shader info into ShaderData
     GLuint textureUnit = mOutGlop->fill.texture.texture ? 1 : 0;
diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h
index c24b122..4b871d5 100644
--- a/libs/hwui/GlopBuilder.h
+++ b/libs/hwui/GlopBuilder.h
@@ -57,11 +57,19 @@
     GlopBuilder& setFillClear();
     GlopBuilder& setFillLayer(Texture& texture, const SkColorFilter* colorFilter,
             float alpha, SkXfermode::Mode mode);
+    GlopBuilder& setFillTextureLayer(Layer& layer, float alpha);
 
     GlopBuilder& setTransform(const Matrix4& ortho, const Matrix4& transform, bool fudgingOffset);
 
     GlopBuilder& setModelViewMapUnitToRect(const Rect destination);
     GlopBuilder& setModelViewMapUnitToRectSnap(const Rect destination);
+    GlopBuilder& setModelViewMapUnitToRectOptionalSnap(bool snap, const Rect destination) {
+        if (snap) {
+            return setModelViewMapUnitToRectSnap(destination);
+        } else {
+            return setModelViewMapUnitToRect(destination);
+        }
+    }
     GlopBuilder& setModelViewOffsetRect(float offsetX, float offsetY, const Rect source);
     GlopBuilder& setModelViewOffsetRectSnap(float offsetX, float offsetY, const Rect source);
 
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index f598664..30ffcea 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -356,8 +356,9 @@
 
 bool LayerRenderer::copyLayer(RenderState& renderState, Layer* layer, SkBitmap* bitmap) {
     Caches& caches = Caches::getInstance();
-    if (layer && bitmap->width() <= caches.maxTextureSize &&
-            bitmap->height() <= caches.maxTextureSize) {
+    if (layer
+            && bitmap->width() <= caches.maxTextureSize
+            && bitmap->height() <= caches.maxTextureSize) {
 
         GLuint fbo = caches.fboCache.get();
         if (!fbo) {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 3913ee5..83715ba 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -846,8 +846,21 @@
 }
 
 void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) {
-    float alpha = getLayerAlpha(layer);
+    if (USE_GLOPS) {
+        bool snap = !layer->getForceFilter()
+                && layer->getWidth() == (uint32_t) rect.getWidth()
+                && layer->getHeight() == (uint32_t) rect.getHeight();
+        Glop glop;
+        GlopBuilder aBuilder(mRenderState, mCaches, &glop);
+        aBuilder.setMeshTexturedUvQuad(nullptr, Rect(0, 1, 1, 0)) // TODO: simplify with VBO
+                .setFillTextureLayer(*layer, getLayerAlpha(layer))
+                .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
+                .setModelViewMapUnitToRectOptionalSnap(snap, rect)
+                .setRoundRectClipState(currentSnapshot()->roundRectClipState)
+                .build();
+    }
 
+    float alpha = getLayerAlpha(layer);
     setupDraw();
     if (layer->getRenderTarget() == GL_TEXTURE_2D) {
         setupDrawWithTexture();
@@ -866,10 +879,10 @@
     } else {
         setupDrawExternalTexture(layer->getTextureId());
     }
-    if (currentTransform()->isPureTranslate() &&
-            !layer->getForceFilter() &&
-            layer->getWidth() == (uint32_t) rect.getWidth() &&
-            layer->getHeight() == (uint32_t) rect.getHeight()) {
+    if (currentTransform()->isPureTranslate()
+            && !layer->getForceFilter()
+            && layer->getWidth() == (uint32_t) rect.getWidth()
+            && layer->getHeight() == (uint32_t) rect.getHeight()) {
         const float x = (int) floorf(rect.left + currentTransform()->getTranslateX() + 0.5f);
         const float y = (int) floorf(rect.top + currentTransform()->getTranslateY() + 0.5f);
 
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index 7992077..ca3a4c2 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -260,19 +260,25 @@
     meshState().bindIndicesBufferInternal(indices.bufferObject);
 
     if (vertices.flags & VertexAttribFlags::kTextureCoord) {
-        // fill.texture always takes slot 0, shader samplers increment from there
+        const Glop::Fill::TextureData& texture = fill.texture;
+        // texture always takes slot 0, shader samplers increment from there
         mCaches->textureState().activateTexture(0);
 
-        if (fill.texture.clamp != GL_INVALID_ENUM) {
-            fill.texture.texture->setWrap(fill.texture.clamp, true);
+        if (texture.clamp != GL_INVALID_ENUM) {
+            texture.texture->setWrap(texture.clamp, true);
         }
-        if (fill.texture.filter != GL_INVALID_ENUM) {
-            fill.texture.texture->setFilter(fill.texture.filter, true);
+        if (texture.filter != GL_INVALID_ENUM) {
+            texture.texture->setFilter(texture.filter, true);
         }
 
-        mCaches->textureState().bindTexture(fill.texture.texture->id);
+        mCaches->textureState().bindTexture(texture.target, texture.texture->id);
         meshState().enableTexCoordsVertexArray();
         meshState().bindTexCoordsVertexPointer(force, vertices.texCoord, vertices.stride);
+
+        if (texture.textureTransform) {
+            glUniformMatrix4fv(fill.program->getUniform("mainTextureTransform"), 1,
+                    GL_FALSE, &texture.textureTransform->data[0]);
+        }
     } else {
         meshState().disableTexCoordsVertexArray();
     }
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 0aa8862..f869841 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -16,8 +16,6 @@
 
 package com.android.server.usb;
 
-import android.alsa.AlsaCardsParser;
-import android.alsa.AlsaDevicesParser;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
@@ -37,6 +35,8 @@
 import android.provider.Settings;
 import android.util.Slog;
 
+import com.android.internal.alsa.AlsaCardsParser;
+import com.android.internal.alsa.AlsaDevicesParser;
 import com.android.server.audio.AudioService;
 
 import libcore.io.IoUtils;