Allows to render with an OpenGL context inside a TextureView.

Change-Id: I59453f7fc3997f0502a1c5d325d37fed376fabc7
diff --git a/api/current.txt b/api/current.txt
index a98ffae..a7f0368 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -21025,6 +21025,7 @@
 
   public static abstract interface TextureView.SurfaceTextureListener {
     method public abstract void onSurfaceTextureAvailable(android.graphics.SurfaceTexture);
+    method public abstract void onSurfaceTextureSizeChanged(android.graphics.SurfaceTexture, int, int);
   }
 
   public class TouchDelegate {
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index cdf8954..984102a 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -163,8 +163,7 @@
     static native int nCreateTextureLayer(int[] layerInfo);
     static native int nCreateLayer(int width, int height, boolean isOpaque, int[] layerInfo);
     static native void nResizeLayer(int layerId, int width, int height, int[] layerInfo);
-    static native void nUpdateTextureLayer(int layerId, int width, int height,
-            float[] textureTransform);
+    static native void nUpdateTextureLayer(int layerId, int width, int height, int surface);
     static native void nDestroyLayer(int layerId);
     static native void nDestroyLayerDeferred(int layerId);
 
diff --git a/core/java/android/view/GLES20TextureLayer.java b/core/java/android/view/GLES20TextureLayer.java
index 21fbdfcf6..fcf421b 100644
--- a/core/java/android/view/GLES20TextureLayer.java
+++ b/core/java/android/view/GLES20TextureLayer.java
@@ -70,7 +70,7 @@
         return mSurface;
     }
 
-    void update(int width, int height, float[] textureTransform) {
-        GLES20Canvas.nUpdateTextureLayer(mLayer, width, height, textureTransform);
+    void update(int width, int height, int surface) {
+        GLES20Canvas.nUpdateTextureLayer(mLayer, width, height, surface);
     }
 }
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 5b2983d..fc4ba3f 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -202,11 +202,10 @@
      * @param layer The hardware layer to update
      * @param width The layer's width
      * @param height The layer's height
-     * @param textureTransform A 4x4 column-first transform matrix to apply to
-     *        texture coordinates
+     * @param surface The surface to update
      */
     abstract void updateTextureLayer(HardwareLayer layer, int width, int height,
-            float[] textureTransform);    
+            SurfaceTexture surface);    
     
     /**
      * Initializes the hardware renderer for the specified surface and setup the
@@ -906,8 +905,8 @@
 
         @Override
         void updateTextureLayer(HardwareLayer layer, int width, int height,
-                float[] textureTransform) {
-            ((GLES20TextureLayer) layer).update(width, height, textureTransform);
+                SurfaceTexture surface) {
+            ((GLES20TextureLayer) layer).update(width, height, surface.mSurfaceTexture);
         }
 
         static HardwareRenderer create(boolean translucent) {
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 6380e1b..755ecf5 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -73,6 +73,10 @@
  *              // Something bad happened
  *          }
  *      }
+ *      
+ *      public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+ *          // Ignored, Camera does all the work for us
+ *      }
  *  }
  * </pre>
  * 
@@ -90,8 +94,6 @@
     private HardwareLayer mLayer;
     private SurfaceTexture mSurface;
     private SurfaceTextureListener mListener;
-    
-    private final float[] mTextureTransform = new float[16];
 
     private final Runnable mUpdateLayerAction = new Runnable() {
         @Override
@@ -99,6 +101,7 @@
             updateLayer();
         }
     };
+    private SurfaceTexture.OnFrameAvailableListener mUpdateListener;
 
     /**
      * Creates a new TextureView.
@@ -210,6 +213,14 @@
     }
 
     @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+        if (mSurface != null) {
+            nSetDefaultBufferSize(mSurface.mSurfaceTexture, getWidth(), getHeight());
+        }
+    }
+
+    @Override
     HardwareLayer getHardwareLayer() {
         if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) {
             return null;
@@ -218,15 +229,17 @@
         if (mLayer == null) {
             mLayer = mAttachInfo.mHardwareRenderer.createHardwareLayer();
             mSurface = mAttachInfo.mHardwareRenderer.createSuraceTexture(mLayer);
+            nSetDefaultBufferSize(mSurface.mSurfaceTexture, getWidth(), getHeight());
 
-            mSurface.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() {
+            mUpdateListener = new SurfaceTexture.OnFrameAvailableListener() {
                 @Override
                 public void onFrameAvailable(SurfaceTexture surfaceTexture) {
                     // Per SurfaceTexture's documentation, the callback may be invoked
                     // from an arbitrary thread
                     post(mUpdateLayerAction);
                 }
-            });
+            };
+            mSurface.setOnFrameAvailableListener(mUpdateListener);
 
             if (mListener != null) {
                 mListener.onSurfaceTextureAvailable(mSurface);
@@ -236,16 +249,29 @@
         return mLayer;
     }
 
+    @Override
+    protected void onVisibilityChanged(View changedView, int visibility) {
+        super.onVisibilityChanged(changedView, visibility);
+
+        if (mSurface != null) {
+            // When the view becomes invisible, stop updating it, it's a waste of CPU
+            // To cancel updates, the easiest thing to do is simply to remove the
+            // updates listener
+            if (visibility == VISIBLE) {
+                mSurface.setOnFrameAvailableListener(mUpdateListener);
+                updateLayer();
+            } else {
+                mSurface.setOnFrameAvailableListener(null);
+            }
+        }
+    }
+
     private void updateLayer() {
         if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) {
             return;
         }
 
-        mSurface.updateTexImage();
-        mSurface.getTransformMatrix(mTextureTransform);
-
-        mAttachInfo.mHardwareRenderer.updateTextureLayer(mLayer, getWidth(), getHeight(),
-                mTextureTransform);
+        mAttachInfo.mHardwareRenderer.updateTextureLayer(mLayer, getWidth(), getHeight(), mSurface);
 
         invalidate();
     }
@@ -292,5 +318,17 @@
          *                {@link android.view.TextureView#getSurfaceTexture()}
          */
         public void onSurfaceTextureAvailable(SurfaceTexture surface);
+
+        /**
+         * Invoked when the {@link SurfaceTexture}'s buffers size changed.
+         * 
+         * @param surface The surface returned by
+         *                {@link android.view.TextureView#getSurfaceTexture()}
+         * @param width The new width of the surface
+         * @param height The new height of the surface
+         */
+        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height);
     }
+
+    private static native void nSetDefaultBufferSize(int surfaceTexture, int width, int height);
 }
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 290f528..95224a4 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -47,6 +47,7 @@
 	android_emoji_EmojiFactory.cpp \
 	android_view_Display.cpp \
 	android_view_Surface.cpp \
+	android_view_TextureView.cpp \
 	android_view_ViewRoot.cpp \
 	android_view_InputChannel.cpp \
 	android_view_InputQueue.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index a4a229a..c915753 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -118,6 +118,7 @@
 extern int register_android_view_Display(JNIEnv* env);
 extern int register_android_view_GLES20Canvas(JNIEnv* env);
 extern int register_android_view_Surface(JNIEnv* env);
+extern int register_android_view_TextureView(JNIEnv* env);
 extern int register_android_view_ViewRoot(JNIEnv* env);
 extern int register_android_database_CursorWindow(JNIEnv* env);
 extern int register_android_database_SQLiteCompiledSql(JNIEnv* env);
@@ -1122,6 +1123,7 @@
     REG_JNI(register_android_graphics_Graphics),
     REG_JNI(register_android_view_GLES20Canvas),
     REG_JNI(register_android_view_Surface),
+    REG_JNI(register_android_view_TextureView),
     REG_JNI(register_android_view_ViewRoot),
     REG_JNI(register_com_google_android_gles_jni_EGLImpl),
     REG_JNI(register_com_google_android_gles_jni_GLImpl),
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index af7639a..f929a0e 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -24,6 +24,8 @@
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/ResourceTypes.h>
 
+#include <gui/SurfaceTexture.h>
+
 #include <SkBitmap.h>
 #include <SkCanvas.h>
 #include <SkMatrix.h>
@@ -577,10 +579,13 @@
 }
 
 static void android_view_GLES20Canvas_updateTextureLayer(JNIEnv* env, jobject clazz,
-        Layer* layer, jint width, jint height, jfloatArray texTransform) {
-    jfloat* transform = env->GetFloatArrayElements(texTransform, NULL);
-    LayerRenderer::updateTextureLayer(layer, width, height, transform);
-    env->ReleaseFloatArrayElements(texTransform, transform, 0);
+        Layer* layer, jint width, jint height, SurfaceTexture* surface) {
+    float transform[16];
+    surface->updateTexImage();
+    surface->getTransformMatrix(transform);
+    GLenum renderTarget = surface->getCurrentTextureTarget();
+
+    LayerRenderer::updateTextureLayer(layer, width, height, renderTarget, transform);
 }
 
 static void android_view_GLES20Canvas_destroyLayer(JNIEnv* env, jobject clazz, Layer* layer) {
@@ -717,7 +722,7 @@
     { "nCreateLayer",            "(IIZ[I)I",   (void*) android_view_GLES20Canvas_createLayer },
     { "nResizeLayer",            "(III[I)V" ,  (void*) android_view_GLES20Canvas_resizeLayer },
     { "nCreateTextureLayer",     "([I)I",      (void*) android_view_GLES20Canvas_createTextureLayer },
-    { "nUpdateTextureLayer",     "(III[F)V" ,  (void*) android_view_GLES20Canvas_updateTextureLayer },
+    { "nUpdateTextureLayer",     "(IIII)V",    (void*) android_view_GLES20Canvas_updateTextureLayer },
     { "nDestroyLayer",           "(I)V",       (void*) android_view_GLES20Canvas_destroyLayer },
     { "nDestroyLayerDeferred",   "(I)V",       (void*) android_view_GLES20Canvas_destroyLayerDeferred },
     { "nDrawLayer",              "(IIFFI)V",   (void*) android_view_GLES20Canvas_drawLayer },
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
new file mode 100644
index 0000000..c5d86c8
--- /dev/null
+++ b/core/jni/android_view_TextureView.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jni.h"
+#include <nativehelper/JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+
+#include <gui/SurfaceTexture.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+// Native layer
+// ----------------------------------------------------------------------------
+
+static void android_view_TextureView_setDefaultBufferSize(JNIEnv* env, jobject,
+    jint surfaceTexture, jint width, jint height) {
+
+    sp<SurfaceTexture> surface = reinterpret_cast<SurfaceTexture*>(surfaceTexture);
+    surface->setDefaultBufferSize(width, height);
+}
+
+// ----------------------------------------------------------------------------
+// JNI Glue
+// ----------------------------------------------------------------------------
+
+const char* const kClassPathName = "android/view/TextureView";
+
+static JNINativeMethod gMethods[] = {
+    {   "nSetDefaultBufferSize", "(III)V", (void*) android_view_TextureView_setDefaultBufferSize }
+};
+
+int register_android_view_TextureView(JNIEnv* env) {
+    return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
+}
+
+};
diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
index 5f2065a..f777527 100644
--- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
@@ -27,6 +27,9 @@
 #include <SkBitmap.h>
 #include <SkPixelRef.h>
 
+#include <gui/SurfaceTexture.h>
+#include <gui/SurfaceTextureClient.h>
+
 namespace android {
 
 static jclass gConfig_class;
@@ -319,6 +322,35 @@
     return (jint)sur;
 }
 
+static jint jni_eglCreateWindowSurfaceTexture(JNIEnv *_env, jobject _this, jobject display,
+        jobject config, jint native_window, jintArray attrib_list) {
+    if (display == NULL || config == NULL
+        || !validAttribList(_env, attrib_list)) {
+        jniThrowException(_env, "java/lang/IllegalArgumentException", NULL);
+        return JNI_FALSE;
+    }
+    EGLDisplay dpy = getDisplay(_env, display);
+    EGLContext cnf = getConfig(_env, config);
+    sp<ANativeWindow> window;
+    if (native_window == 0) {
+not_valid_surface:
+        jniThrowException(_env, "java/lang/IllegalArgumentException",
+                "Make sure the SurfaceTexture is valid");
+        return 0;
+    }
+    
+    sp<SurfaceTexture> surfaceTexture = reinterpret_cast<SurfaceTexture*>(native_window);
+
+    window = new SurfaceTextureClient(surfaceTexture);
+    if (window == NULL)
+        goto not_valid_surface;
+
+    jint* base = beginNativeAttribList(_env, attrib_list);
+    EGLSurface sur = eglCreateWindowSurface(dpy, cnf, window.get(), base);
+    endNativeAttributeList(_env, attrib_list, base);
+    return (jint)sur;
+}
+
 static jboolean jni_eglGetConfigAttrib(JNIEnv *_env, jobject _this, jobject display,
         jobject config, jint attribute, jintArray value) {
     if (display == NULL || config == NULL
@@ -508,6 +540,7 @@
 {"_eglCreatePbufferSurface","(" DISPLAY CONFIG "[I)I", (void*)jni_eglCreatePbufferSurface },
 {"_eglCreatePixmapSurface", "(" SURFACE DISPLAY CONFIG OBJECT "[I)V", (void*)jni_eglCreatePixmapSurface },
 {"_eglCreateWindowSurface", "(" DISPLAY CONFIG OBJECT "[I)I", (void*)jni_eglCreateWindowSurface },
+{"_eglCreateWindowSurfaceTexture", "(" DISPLAY CONFIG "I[I)I", (void*)jni_eglCreateWindowSurfaceTexture },
 {"eglDestroyContext",      "(" DISPLAY CONTEXT ")Z", (void*)jni_eglDestroyContext },
 {"eglDestroySurface",      "(" DISPLAY SURFACE ")Z", (void*)jni_eglDestroySurface },
 {"eglMakeCurrent",         "(" DISPLAY SURFACE SURFACE CONTEXT")Z", (void*)jni_eglMakeCurrent },
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index cfae0c1..3c43a39 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -34,8 +34,9 @@
  * the stream to be skipped.
  *
  * <p>When sampling from the texture one should first transform the texture coordinates using the
- * matrix queried via {@link #getTransformMatrix}.  The transform matrix may change each time {@link
- * #updateTexImage} is called, so it should be re-queried each time the texture image is updated.
+ * matrix queried via {@link #getTransformMatrix(float[])}.  The transform matrix may change each
+ * time {@link #updateTexImage} is called, so it should be re-queried each time the texture image
+ * is updated.
  * This matrix transforms traditional 2D OpenGL ES texture coordinate column vectors of the form (s,
  * t, 0, 1) where s and t are on the inclusive interval [0, 1] to the proper sampling location in
  * the streamed texture.  This transform compensates for any properties of the image stream source
@@ -63,8 +64,13 @@
     private EventHandler mEventHandler;
     private OnFrameAvailableListener mOnFrameAvailableListener;
 
-    @SuppressWarnings("unused")
-    private int mSurfaceTexture;
+    /**
+     * This field is used by native code, do not access or modify.
+     * 
+     * @hide
+     */
+    @SuppressWarnings({"UnusedDeclaration"})
+    public int mSurfaceTexture;
 
     /**
      * Callback interface for being notified that a new stream frame is available.
@@ -176,10 +182,13 @@
             if (mOnFrameAvailableListener != null) {
                 mOnFrameAvailableListener.onFrameAvailable(SurfaceTexture.this);
             }
-            return;
         }
     }
 
+    /**
+     * This method is invoked from native code only.
+     */
+    @SuppressWarnings({"UnusedDeclaration"})
     private static void postEventFromNative(Object selfRef) {
         WeakReference weakSelf = (WeakReference)selfRef;
         SurfaceTexture st = (SurfaceTexture)weakSelf.get();
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 16566b8..0310bc3 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -47,6 +47,7 @@
         meshElementCount = 0;
         isCacheable = true;
         isTextureLayer = false;
+        renderTarget = GL_TEXTURE_2D;
     }
 
     ~Layer() {
@@ -155,6 +156,11 @@
      * Optional texture coordinates transform.
      */
     mat4 texTransform;
+
+    /**
+     * Indicates the render target.
+     */
+    GLenum renderTarget;
 }; // struct Layer
 
 }; // namespace uirenderer
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index e167336..f316ba7 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -185,17 +185,7 @@
     layer->region.clear();
 
     glActiveTexture(GL_TEXTURE0);
-
     glGenTextures(1, &layer->texture);
-    glBindTexture(GL_TEXTURE_EXTERNAL_OES, layer->texture);
-
-    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
-
-    glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-    glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
-    glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-    glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 
     return layer;
 }
@@ -280,7 +270,7 @@
 }
 
 void LayerRenderer::updateTextureLayer(Layer* layer, uint32_t width, uint32_t height,
-        float* transform) {
+        GLenum renderTarget, float* transform) {
     if (layer) {
         layer->width = width;
         layer->height = height;
@@ -288,6 +278,15 @@
         layer->region.set(width, height);
         layer->regionRect.set(0.0f, 0.0f, width, height);
         layer->texTransform.load(transform);
+        layer->renderTarget = renderTarget;
+
+        glBindTexture(layer->renderTarget, layer->texture);
+
+        glTexParameteri(layer->renderTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+        glTexParameteri(layer->renderTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+        glTexParameteri(layer->renderTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+        glTexParameteri(layer->renderTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
     }
 }
 
diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h
index b3cd5db..59cab96 100644
--- a/libs/hwui/LayerRenderer.h
+++ b/libs/hwui/LayerRenderer.h
@@ -57,7 +57,7 @@
     static Layer* createLayer(uint32_t width, uint32_t height, bool isOpaque = false);
     static bool resizeLayer(Layer* layer, uint32_t width, uint32_t height);
     static void updateTextureLayer(Layer* layer, uint32_t width, uint32_t height,
-            float* transform);
+            GLenum renderTarget, float* transform);
     static void destroyLayer(Layer* layer);
     static void destroyLayerDeferred(Layer* layer);
 
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 34d8fd3..71976cd 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -637,7 +637,12 @@
     float alpha = layer->alpha / 255.0f;
 
     setupDraw();
-    setupDrawWithExternalTexture();
+    if (layer->renderTarget == GL_TEXTURE_2D) {
+        setupDrawWithTexture();
+    } else {
+        setupDrawWithExternalTexture();
+    }
+    setupDrawTextureTransform();
     setupDrawColor(alpha, alpha, alpha, alpha);
     setupDrawColorFilter();
     setupDrawBlending(layer->blend, layer->mode);
@@ -645,8 +650,12 @@
     setupDrawModelView(rect.left, rect.top, rect.right, rect.bottom);
     setupDrawPureColorUniforms();
     setupDrawColorFilterUniforms();
-    setupDrawExternalTexture(layer->texture);
-    setupDrawTextureTransform(layer->texTransform);
+    if (layer->renderTarget == GL_TEXTURE_2D) {
+        setupDrawTexture(layer->texture);
+    } else {
+        setupDrawExternalTexture(layer->texture);
+    }
+    setupDrawTextureTransformUniforms(layer->texTransform);
     setupDrawMesh(&mMeshVertices[0].position[0], &mMeshVertices[0].texture[0]);
 
     glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
@@ -1095,7 +1104,11 @@
     glEnableVertexAttribArray(mTexCoordsSlot);
 }
 
-void OpenGLRenderer::setupDrawTextureTransform(mat4& transform) {
+void OpenGLRenderer::setupDrawTextureTransform() {
+    mDescription.hasTextureTransform = true;
+}
+
+void OpenGLRenderer::setupDrawTextureTransformUniforms(mat4& transform) {
     glUniformMatrix4fv(mCaches.currentProgram->getUniform("mainTextureTransform"), 1,
             GL_FALSE, &transform.data[0]);
 }
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 0ffd70b..22309f8 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -465,7 +465,8 @@
     void setupDrawSimpleMesh();
     void setupDrawTexture(GLuint texture);
     void setupDrawExternalTexture(GLuint texture);
-    void setupDrawTextureTransform(mat4& transform);
+    void setupDrawTextureTransform();
+    void setupDrawTextureTransformUniforms(mat4& transform);
     void setupDrawMesh(GLvoid* vertices, GLvoid* texCoords = NULL, GLuint vbo = 0);
     void setupDrawVertices(GLvoid* vertices);
     void setupDrawAALine(GLvoid* vertices, GLvoid* distanceCoords, float strokeWidth);
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 62ac2ba..297f5a9 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -385,7 +385,7 @@
     }
     // Uniforms
     shader.append(gVS_Header_Uniforms);
-    if (description.hasExternalTexture) {
+    if (description.hasTextureTransform) {
         shader.append(gVS_Header_Uniforms_TextureTransform);
     }
     if (description.hasGradient) {
@@ -415,11 +415,10 @@
 
     // Begin the shader
     shader.append(gVS_Main); {
-        if (description.hasTexture) {
-            shader.append(gVS_Main_OutTexCoords);
-        }
-        if (description.hasExternalTexture) {
+        if (description.hasTextureTransform) {
             shader.append(gVS_Main_OutTransformedTexCoords);
+        } else if (description.hasTexture || description.hasExternalTexture) {
+            shader.append(gVS_Main_OutTexCoords);
         }
         if (description.hasWidth) {
             shader.append(gVS_Main_Width);
@@ -487,8 +486,7 @@
     }
     if (description.hasTexture) {
         shader.append(gFS_Uniforms_TextureSampler);
-    }
-    if (description.hasExternalTexture) {
+    } else if (description.hasExternalTexture) {
         shader.append(gFS_Uniforms_ExternalTextureSampler);
     }
     if (description.hasWidth) {
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
index 70909fd..21eb099 100644
--- a/libs/hwui/ProgramCache.h
+++ b/libs/hwui/ProgramCache.h
@@ -78,6 +78,7 @@
 #define PROGRAM_HAS_WIDTH_SHIFT 37
 
 #define PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT 38
+#define PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT 39
 
 ///////////////////////////////////////////////////////////////////////////////
 // Types
@@ -116,6 +117,7 @@
     bool hasTexture;
     bool hasAlpha8Texture;
     bool hasExternalTexture;
+    bool hasTextureTransform;
 
     // Modulate, this should only be set when setColor() return true
     bool modulate;
@@ -155,6 +157,7 @@
         hasTexture = false;
         hasAlpha8Texture = false;
         hasExternalTexture = false;
+        hasTextureTransform = false;
 
         hasWidth = false;
 
@@ -245,6 +248,7 @@
         if (isPoint) key |= programid(0x1) << PROGRAM_IS_POINT_SHIFT;
         if (hasWidth) key |= programid(0x1) << PROGRAM_HAS_WIDTH_SHIFT;
         if (hasExternalTexture) key |= programid(0x1) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT;
+        if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT;
         return key;
     }
 
diff --git a/opengl/java/com/google/android/gles_jni/EGLImpl.java b/opengl/java/com/google/android/gles_jni/EGLImpl.java
index 8a7124d..f162d40 100644
--- a/opengl/java/com/google/android/gles_jni/EGLImpl.java
+++ b/opengl/java/com/google/android/gles_jni/EGLImpl.java
@@ -18,10 +18,10 @@
 
 import javax.microedition.khronos.egl.*;
 
+import android.graphics.SurfaceTexture;
 import android.view.Surface;
 import android.view.SurfaceView;
 import android.view.SurfaceHolder;
-import android.view.View;
 
 public class EGLImpl implements EGL10 {
     private EGLContextImpl mContext = new EGLContextImpl(-1);
@@ -71,19 +71,28 @@
     }
 
     public EGLSurface eglCreateWindowSurface(EGLDisplay display, EGLConfig config, Object native_window, int[] attrib_list) {
-        Surface sur;
+        Surface sur = null;
         if (native_window instanceof SurfaceView) {
             SurfaceView surfaceView = (SurfaceView)native_window;
             sur = surfaceView.getHolder().getSurface();
         } else if (native_window instanceof SurfaceHolder) {
             SurfaceHolder holder = (SurfaceHolder)native_window;
             sur = holder.getSurface();
+        }
+
+        int eglSurfaceId;
+        if (sur != null) {
+            eglSurfaceId = _eglCreateWindowSurface(display, config, sur, attrib_list);
+        } else if (native_window instanceof SurfaceTexture) {
+            eglSurfaceId = _eglCreateWindowSurfaceTexture(display, config,
+                    ((SurfaceTexture) native_window).mSurfaceTexture, attrib_list);
         } else {
             throw new java.lang.UnsupportedOperationException(
                 "eglCreateWindowSurface() can only be called with an instance of " +
-                "SurfaceView or SurfaceHolder at the moment, this will be fixed later.");
+                "SurfaceView, SurfaceHolder or SurfaceTexture at the moment, " + 
+                "this will be fixed later.");
         }
-        int eglSurfaceId = _eglCreateWindowSurface(display, config, sur, attrib_list);
+
         if (eglSurfaceId == 0) {
             return EGL10.EGL_NO_SURFACE;
         }
@@ -134,6 +143,7 @@
     private native int _eglCreatePbufferSurface(EGLDisplay display, EGLConfig config, int[] attrib_list);
     private native void _eglCreatePixmapSurface(EGLSurface sur, EGLDisplay display, EGLConfig config, Object native_pixmap, int[] attrib_list);
     private native int _eglCreateWindowSurface(EGLDisplay display, EGLConfig config, Object native_window, int[] attrib_list);
+    private native int _eglCreateWindowSurfaceTexture(EGLDisplay display, EGLConfig config, int native_window, int[] attrib_list);
     private native int _eglGetDisplay(Object native_display);
     private native int _eglGetCurrentContext();
     private native int _eglGetCurrentDisplay();
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index c5c3f70..6285880 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -37,6 +37,15 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+
+        <activity
+                android:name="GLTextureViewActivity"
+                android:label="_TextureViewGL">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
         
         <activity
                 android:name="BitmapMeshActivity"
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java
new file mode 100644
index 0000000..7f97098
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.app.Activity;
+import android.graphics.SurfaceTexture;
+import android.opengl.GLES20;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.TextureView;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGL11;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.egl.EGLSurface;
+import javax.microedition.khronos.opengles.GL;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class GLTextureViewActivity extends Activity implements TextureView.SurfaceTextureListener {
+    private RenderThread mRenderThread;
+    private TextureView mTextureView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mTextureView = new TextureView(this);
+        mTextureView.setSurfaceTextureListener(this);
+
+        setContentView(mTextureView, new FrameLayout.LayoutParams(500, 400, Gravity.CENTER));
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        mRenderThread.finish();
+    }
+
+    @Override
+    public void onSurfaceTextureAvailable(SurfaceTexture surface) {
+        mRenderThread = new RenderThread(surface);
+        mRenderThread.start();
+
+        mTextureView.setCameraDistance(5000);
+
+        ObjectAnimator animator = ObjectAnimator.ofFloat(mTextureView, "rotationY", 0.0f, 360.0f);
+        animator.setRepeatMode(ObjectAnimator.REVERSE);
+        animator.setRepeatCount(ObjectAnimator.INFINITE);
+        animator.setDuration(4000);
+        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                ((View) mTextureView.getParent()).invalidate();
+            }
+        });
+        animator.start();
+    }
+
+    @Override
+    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+    }
+
+    private static class RenderThread extends Thread {
+        private static final String LOG_TAG = "GLTextureView";
+
+        static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+        static final int EGL_SURFACE_TYPE = 0x3033;
+        static final int EGL_SWAP_BEHAVIOR_PRESERVED_BIT = 0x0400;
+        static final int EGL_OPENGL_ES2_BIT = 4;
+
+        private volatile boolean mFinished;
+
+        private SurfaceTexture mSurface;
+        
+        private EGL10 mEgl;
+        private EGLDisplay mEglDisplay;
+        private EGLConfig mEglConfig;
+        private EGLContext mEglContext;
+        private EGLSurface mEglSurface;
+        private GL mGL;
+
+        RenderThread(SurfaceTexture surface) {
+            mSurface = surface;
+        }
+
+        @Override
+        public void run() {
+            initGL();
+
+            float red = 0.0f;
+            while (!mFinished) {
+                checkCurrent();
+
+                GLES20.glClearColor(red, 0.0f, 0.0f, 1.0f);
+                int error = GLES20.glGetError();
+                if (error != GLES20.GL_NO_ERROR) {
+                    Log.w(LOG_TAG, "GL error = 0x" + Integer.toHexString(error));
+                }
+
+                GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+                error = GLES20.glGetError();
+                if (error != GLES20.GL_NO_ERROR) {
+                    Log.w(LOG_TAG, "GL error = 0x" + Integer.toHexString(error));
+                }
+
+                if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
+                    throw new RuntimeException("Cannot swap buffers");
+                }
+                
+                try {
+                    Thread.sleep(20);
+                } catch (InterruptedException e) {
+                    // Ignore
+                }
+
+                red += 0.021f;
+                if (red > 1.0f) red = 0.0f;
+            }
+
+            finishGL();
+        }
+
+        private void finishGL() {
+            mEgl.eglDestroyContext(mEglDisplay, mEglContext);
+            mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
+        }
+
+        private void checkCurrent() {
+            if (!mEglContext.equals(mEgl.eglGetCurrentContext()) ||
+                    !mEglSurface.equals(mEgl.eglGetCurrentSurface(EGL10.EGL_DRAW))) {
+                if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+                    throw new RuntimeException("eglMakeCurrent failed "
+                            + getEGLErrorString(mEgl.eglGetError()));
+                }
+            }
+        }
+        
+        private void initGL() {
+            mEgl = (EGL10) EGLContext.getEGL();
+
+            mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+            if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
+                throw new RuntimeException("eglGetDisplay failed "
+                        + getEGLErrorString(mEgl.eglGetError()));
+            }
+            
+            int[] version = new int[2];
+            if (!mEgl.eglInitialize(mEglDisplay, version)) {
+                throw new RuntimeException("eglInitialize failed " +
+                        getEGLErrorString(mEgl.eglGetError()));
+            }
+
+            mEglConfig = chooseEglConfig();
+            if (mEglConfig == null) {
+                throw new RuntimeException("eglConfig not initialized");
+            }
+            
+            mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
+
+            mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface, null);
+
+            if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
+                int error = mEgl.eglGetError();
+                if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
+                    Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
+                    return;
+                }
+                throw new RuntimeException("createWindowSurface failed "
+                        + getEGLErrorString(error));
+            }
+
+            if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+                throw new RuntimeException("eglMakeCurrent failed "
+                        + getEGLErrorString(mEgl.eglGetError()));
+            }
+
+            mGL = mEglContext.getGL();
+        }
+        
+
+        EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
+            int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
+            return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);            
+        }
+
+        private EGLConfig chooseEglConfig() {
+            int[] configsCount = new int[1];
+            EGLConfig[] configs = new EGLConfig[1];
+            int[] configSpec = getConfig();
+            if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) {
+                throw new IllegalArgumentException("eglChooseConfig failed " +
+                        getEGLErrorString(mEgl.eglGetError()));
+            } else if (configsCount[0] > 0) {
+                return configs[0];
+            }
+            return null;
+        }
+        
+        private int[] getConfig() {
+            return new int[] {
+                    EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+                    EGL10.EGL_RED_SIZE, 8,
+                    EGL10.EGL_GREEN_SIZE, 8,
+                    EGL10.EGL_BLUE_SIZE, 8,
+                    EGL10.EGL_ALPHA_SIZE, 8,
+                    EGL10.EGL_DEPTH_SIZE, 0,
+                    EGL10.EGL_STENCIL_SIZE, 0,
+                    EGL10.EGL_NONE
+            };
+        }
+
+        static String getEGLErrorString(int error) {
+            switch (error) {
+                case EGL10.EGL_SUCCESS:
+                    return "EGL_SUCCESS";
+                case EGL10.EGL_NOT_INITIALIZED:
+                    return "EGL_NOT_INITIALIZED";
+                case EGL10.EGL_BAD_ACCESS:
+                    return "EGL_BAD_ACCESS";
+                case EGL10.EGL_BAD_ALLOC:
+                    return "EGL_BAD_ALLOC";
+                case EGL10.EGL_BAD_ATTRIBUTE:
+                    return "EGL_BAD_ATTRIBUTE";
+                case EGL10.EGL_BAD_CONFIG:
+                    return "EGL_BAD_CONFIG";
+                case EGL10.EGL_BAD_CONTEXT:
+                    return "EGL_BAD_CONTEXT";
+                case EGL10.EGL_BAD_CURRENT_SURFACE:
+                    return "EGL_BAD_CURRENT_SURFACE";
+                case EGL10.EGL_BAD_DISPLAY:
+                    return "EGL_BAD_DISPLAY";
+                case EGL10.EGL_BAD_MATCH:
+                    return "EGL_BAD_MATCH";
+                case EGL10.EGL_BAD_NATIVE_PIXMAP:
+                    return "EGL_BAD_NATIVE_PIXMAP";
+                case EGL10.EGL_BAD_NATIVE_WINDOW:
+                    return "EGL_BAD_NATIVE_WINDOW";
+                case EGL10.EGL_BAD_PARAMETER:
+                    return "EGL_BAD_PARAMETER";
+                case EGL10.EGL_BAD_SURFACE:
+                    return "EGL_BAD_SURFACE";
+                case EGL11.EGL_CONTEXT_LOST:
+                    return "EGL_CONTEXT_LOST";
+                default:
+                    return "0x" + Integer.toHexString(error);
+            }
+        }
+
+        void finish() {
+            mFinished = true;
+        }
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java
index 4726672..2feda57 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java
@@ -84,4 +84,9 @@
         animator.setDuration(4000);
         animator.start();
     }
+
+    @Override
+    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+        // Ignored, the Camera does all the work for us
+    }
 }