Merge "Rework display size access."
diff --git a/api/current.txt b/api/current.txt
index 3492960..c7e6ee6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -13179,6 +13179,7 @@
     method public static void multiplyMM(float[], int, float[], int, float[], int);
     method public static void multiplyMV(float[], int, float[], int, float[], int);
     method public static void orthoM(float[], int, float, float, float, float, float, float);
+    method public static void perspectiveM(float[], int, float, float, float, float);
     method public static void rotateM(float[], int, float[], int, float, float, float, float);
     method public static void rotateM(float[], int, float, float, float, float);
     method public static void scaleM(float[], int, float[], int, float, float, float);
diff --git a/core/java/android/util/JsonReader.java b/core/java/android/util/JsonReader.java
index 563c500..132b595 100644
--- a/core/java/android/util/JsonReader.java
+++ b/core/java/android/util/JsonReader.java
@@ -16,12 +16,13 @@
 
 package android.util;
 
+import java.io.Closeable;
 import java.io.EOFException;
 import java.io.IOException;
 import java.io.Reader;
-import java.io.Closeable;
 import java.util.ArrayList;
 import java.util.List;
+import libcore.internal.StringPool;
 
 /**
  * Reads a JSON (<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>)
@@ -177,6 +178,8 @@
     private static final String TRUE = "true";
     private static final String FALSE = "false";
 
+    private final StringPool stringPool = new StringPool();
+
     /** The input JSON. */
     private final Reader in;
 
@@ -836,7 +839,7 @@
                     if (skipping) {
                         return "skipped!";
                     } else if (builder == null) {
-                        return new String(buffer, start, pos - start - 1);
+                        return stringPool.get(buffer, start, pos - start - 1);
                     } else {
                         builder.append(buffer, start, pos - start - 1);
                         return builder.toString();
@@ -934,7 +937,7 @@
         } else if (skipping) {
             result = "skipped!";
         } else if (builder == null) {
-            result = new String(buffer, pos, i);
+            result = stringPool.get(buffer, pos, i);
         } else {
             builder.append(buffer, pos, i);
             result = builder.toString();
@@ -968,7 +971,7 @@
                 if (pos + 4 > limit && !fillBuffer(4)) {
                     throw syntaxError("Unterminated escape sequence");
                 }
-                String hex = new String(buffer, pos, 4);
+                String hex = stringPool.get(buffer, pos, 4);
                 pos += 4;
                 return (char) Integer.parseInt(hex, 16);
 
@@ -1040,7 +1043,7 @@
             value = FALSE;
             return JsonToken.BOOLEAN;
         } else {
-            value = new String(buffer, valuePos, valueLength);
+            value = stringPool.get(buffer, valuePos, valueLength);
             return decodeNumber(buffer, valuePos, valueLength);
         }
     }
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index b348142..7fba5ca 100644
--- a/core/java/com/android/internal/view/menu/MenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/MenuBuilder.java
@@ -814,8 +814,10 @@
      * many menu operations are going to be performed as a batch.
      */
     public void stopDispatchingItemsChanged() {
-        mPreventDispatchingItemsChanged = true;
-        mItemsChangedWhileDispatchPrevented = false;
+        if (!mPreventDispatchingItemsChanged) {
+            mPreventDispatchingItemsChanged = true;
+            mItemsChangedWhileDispatchPrevented = false;
+        }
     }
 
     public void startDispatchingItemsChanged() {
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fc4272d8..a91c1e0 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -101,10 +101,6 @@
          for backward compatibility with apps that require external storage. -->
     <bool name="config_emulateExternalStorage">false</bool>
 
-    <!-- Set to true if external storage is case sensitive.
-         Typically external storage is FAT, which is case insensitive. -->
-    <bool name="config_caseSensitiveExternalStorage">false</bool>
-    
     <!-- A product with no SD card == not removable. -->
     <bool name="config_externalStorageRemovable" product="nosdcard">false</bool>
     <!-- Configures whether the primary external storage device is
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 7d02cf8..faecadd 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -60,7 +60,10 @@
         FV(1.0f, 1.0f, 1.0f, 1.0f)
 };
 static const GLsizei gMeshStride = sizeof(TextureVertex);
+static const GLsizei gVertexStride = sizeof(Vertex);
+static const GLsizei gAlphaVertexStride = sizeof(AlphaVertex);
 static const GLsizei gMeshTextureOffset = 2 * sizeof(float);
+static const GLsizei gVertexAlphaOffset = 2 * sizeof(float);
 static const GLsizei gMeshCount = 4;
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 6fabbef..7f28959 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -882,6 +882,10 @@
     mDescription.hasAlpha8Texture = isAlpha8;
 }
 
+void OpenGLRenderer::setupDrawAALine() {
+    mDescription.hasWidth = true;
+}
+
 void OpenGLRenderer::setupDrawPoint(float pointSize) {
     mDescription.isPoint = true;
     mDescription.pointSize = pointSize;
@@ -893,6 +897,7 @@
 
 void OpenGLRenderer::setupDrawColor(int color, int alpha) {
     mColorA = alpha / 255.0f;
+    // BUG on this next line? a is alpha divided by 255 *twice*
     const float a = mColorA / 255.0f;
     mColorR = a * ((color >> 16) & 0xFF);
     mColorG = a * ((color >>  8) & 0xFF);
@@ -1060,6 +1065,37 @@
     }
 }
 
+void OpenGLRenderer::setupDrawVertices(GLvoid* vertices) {
+    mCaches.unbindMeshBuffer();
+    glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE,
+            gVertexStride, vertices);
+}
+
+/**
+ * Sets up the shader to draw an AA line. We draw AA lines with quads, where there is an
+ * outer boundary that fades out to 0. The variables set in the shader define the width of the
+ * core line primitive ("width") and the width of the fading boundary ("boundaryWidth"). The
+ * "vtxDistance" attribute (one per vertex) is a value from zero to one that tells the fragment
+ * shader where the fragment is in relation to the line width overall; this value is then used
+ * to compute the proper color, based on whether the fragment lies in the fading AA region of
+ * the line.
+ */
+void OpenGLRenderer::setupDrawAALine(GLvoid* vertices, GLvoid* distanceCoords, float strokeWidth) {
+    mCaches.unbindMeshBuffer();
+    glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE,
+            gAlphaVertexStride, vertices);
+    int distanceSlot = mCaches.currentProgram->getAttrib("vtxDistance");
+    glEnableVertexAttribArray(distanceSlot);
+    glVertexAttribPointer(distanceSlot, 1, GL_FLOAT, GL_FALSE, gAlphaVertexStride, distanceCoords);
+    int widthSlot = mCaches.currentProgram->getUniform("width");
+    int boundaryWidthSlot = mCaches.currentProgram->getUniform("boundaryWidth");
+    int inverseBoundaryWidthSlot = mCaches.currentProgram->getUniform("inverseBoundaryWidth");
+    float boundaryWidth = (1 - strokeWidth) / 2;
+    glUniform1f(widthSlot, strokeWidth);
+    glUniform1f(boundaryWidthSlot, boundaryWidth);
+    glUniform1f(inverseBoundaryWidthSlot, (1 / boundaryWidth));
+}
+
 void OpenGLRenderer::finishDrawTexture() {
     glDisableVertexAttribArray(mTexCoordsSlot);
 }
@@ -1350,114 +1386,199 @@
     }
 }
 
+void OpenGLRenderer::drawLinesAsQuads(float *points, int count, bool isAA, bool isHairline,
+        float strokeWidth) {
+    int verticesCount = count;
+    if (count > 4) {
+        // Polyline: account for extra vertices needed for continous tri-strip
+        verticesCount += (count -4);
+    }
+    if (isAA) {
+        // Expand boundary to enable AA calculations on the quad border
+        strokeWidth += .5f;
+    }
+    Vertex lines[verticesCount];
+    Vertex* vertices = &lines[0];
+    AlphaVertex wLines[verticesCount];
+    AlphaVertex* aaVertices = &wLines[0];
+    if (!isAA) {
+        setupDrawVertices(vertices);
+    } else {
+        void *alphaCoords = ((void*) aaVertices) + gVertexAlphaOffset;
+        // innerProportion is the ratio of the inner (non-AA) port of the line to the total
+        // AA stroke width (the base stroke width expanded by a half pixel on either side).
+        // This value is used in the fragment shader to determine how to fill fragments.
+        float innerProportion = fmax(strokeWidth - 1.0f, 0) / (strokeWidth + .5f);
+        setupDrawAALine((void*) aaVertices, (void*) alphaCoords, innerProportion);
+    }
+
+    int generatedVerticesCount = 0;
+    AlphaVertex *prevAAVertex = NULL;
+    Vertex *prevVertex = NULL;
+    float inverseScaleX, inverseScaleY;
+    if (isHairline) {
+        // The quad that we use for AA hairlines needs to account for scaling because the line
+        // should always be one pixel wide regardless of scale.
+        inverseScaleX = 1.0f;
+        inverseScaleY = 1.0f;
+        if (!mSnapshot->transform->isPureTranslate()) {
+            Matrix4 *mat = mSnapshot->transform;
+            float m00 = mat->data[Matrix4::kScaleX];
+            float m01 = mat->data[Matrix4::kSkewY];
+            float m02 = mat->data[2];
+            float m10 = mat->data[Matrix4::kSkewX];
+            float m11 = mat->data[Matrix4::kScaleX];
+            float m12 = mat->data[6];
+            float scaleX = sqrt(m00*m00 + m01*m01);
+            float scaleY = sqrt(m10*m10 + m11*m11);
+            inverseScaleX = (scaleX != 0) ? (inverseScaleX / scaleX) : 0;
+            inverseScaleY = (scaleY != 0) ? (inverseScaleY / scaleY) : 0;
+        }
+    }
+    for (int i = 0; i < count; i += 4) {
+        // a = start point, b = end point
+        vec2 a(points[i], points[i + 1]);
+        vec2 b(points[i + 2], points[i + 3]);
+
+        // Bias to snap to the same pixels as Skia
+        a += 0.375;
+        b += 0.375;
+
+        // Find the normal to the line
+        vec2 n = (b - a).copyNormalized() * strokeWidth;
+        if (isHairline) {
+            float wideningFactor;
+            if (fabs(n.x) >= fabs(n.y)) {
+                wideningFactor = fabs(1.0f / n.x);
+            } else {
+                wideningFactor = fabs(1.0f / n.y);
+            }
+            n.x *= inverseScaleX;
+            n.y *= inverseScaleY;
+            n *= wideningFactor;
+        }
+        float x = n.x;
+        n.x = -n.y;
+        n.y = x;
+
+        // Four corners of the rectangle defining a thick line
+        vec2 p1 = a - n;
+        vec2 p2 = a + n;
+        vec2 p3 = b + n;
+        vec2 p4 = b - n;
+
+        const float left = fmin(p1.x, fmin(p2.x, fmin(p3.x, p4.x)));
+        const float right = fmax(p1.x, fmax(p2.x, fmax(p3.x, p4.x)));
+        const float top = fmin(p1.y, fmin(p2.y, fmin(p3.y, p4.y)));
+        const float bottom = fmax(p1.y, fmax(p2.y, fmax(p3.y, p4.y)));
+
+        if (!quickReject(left, top, right, bottom)) {
+            // Draw the line as 2 triangles, could be optimized
+            // by using only 4 vertices and the correct indices
+            // Also we should probably used non textured vertices
+            // when line AA is disabled to save on bandwidth
+            if (!isAA) {
+                if (prevVertex != NULL) {
+                    // Issue two repeat vertices to create degenerate triangles to bridge
+                    // between the previous line and the new one. This is necessary because
+                    // we are creating a single triangle_strip which will contain
+                    // potentially discontinuous line segments.
+                    Vertex::set(vertices++, prevVertex->position[0], prevVertex->position[1]);
+                    Vertex::set(vertices++, p1.x, p1.y);
+                    generatedVerticesCount += 2;
+                }
+                Vertex::set(vertices++, p1.x, p1.y);
+                Vertex::set(vertices++, p2.x, p2.y);
+                Vertex::set(vertices++, p4.x, p4.y);
+                Vertex::set(vertices++, p3.x, p3.y);
+                prevVertex = vertices - 1;
+                generatedVerticesCount += 4;
+            } else {
+                if (prevAAVertex != NULL) {
+                    // Issue two repeat vertices to create degenerate triangles to bridge
+                    // between the previous line and the new one. This is necessary because
+                    // we are creating a single triangle_strip which will contain
+                    // potentially discontinuous line segments.
+                    AlphaVertex::set(aaVertices++,prevAAVertex->position[0],
+                            prevAAVertex->position[1], prevAAVertex->alpha);
+                    AlphaVertex::set(aaVertices++, p4.x, p4.y, 1);
+                    generatedVerticesCount += 2;
+                }
+                AlphaVertex::set(aaVertices++, p4.x, p4.y, 1);
+                AlphaVertex::set(aaVertices++, p1.x, p1.y, 1);
+                AlphaVertex::set(aaVertices++, p3.x, p3.y, 0);
+                AlphaVertex::set(aaVertices++, p2.x, p2.y, 0);
+                prevAAVertex = aaVertices - 1;
+                generatedVerticesCount += 4;
+            }
+            dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
+        }
+    }
+    if (generatedVerticesCount > 0) {
+       glDrawArrays(GL_TRIANGLE_STRIP, 0, generatedVerticesCount);
+    }
+}
+
 void OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) {
     if (mSnapshot->isIgnored()) return;
 
     const bool isAA = paint->isAntiAlias();
     const float strokeWidth = paint->getStrokeWidth() * 0.5f;
-    // A stroke width of 0 has a special meaningin Skia:
-    // it draws an unscaled 1px wide line
-    const bool isHairLine = paint->getStrokeWidth() == 0.0f;
+    // A stroke width of 0 has a special meaning in Skia:
+    // it draws a line 1 px wide regardless of current transform
+    bool isHairLine = paint->getStrokeWidth() == 0.0f;
 
     int alpha;
     SkXfermode::Mode mode;
     getAlphaAndMode(paint, &alpha, &mode);
-
-    int verticesCount = count >> 2;
     int generatedVerticesCount = 0;
-    if (!isHairLine) {
-        // TODO: AA needs more vertices
-        verticesCount *= 6;
-    } else {
-        // TODO: AA will be different
-        verticesCount *= 2;
-    }
-
-    TextureVertex lines[verticesCount];
-    TextureVertex* vertex = &lines[0];
 
     setupDraw();
+    if (isAA) {
+        setupDrawAALine();
+    }
     setupDrawColor(paint->getColor(), alpha);
     setupDrawColorFilter();
     setupDrawShader();
-    setupDrawBlending(mode);
+    if (isAA) {
+        setupDrawBlending(true, mode);
+    } else {
+        setupDrawBlending(mode);
+    }
     setupDrawProgram();
     setupDrawModelViewIdentity();
     setupDrawColorUniforms();
     setupDrawColorFilterUniforms();
     setupDrawShaderIdentityUniforms();
-    setupDrawMesh(vertex);
 
     if (!isHairLine) {
-        // TODO: Handle the AA case
-        for (int i = 0; i < count; i += 4) {
-            // a = start point, b = end point
-            vec2 a(points[i], points[i + 1]);
-            vec2 b(points[i + 2], points[i + 3]);
-
-            // Bias to snap to the same pixels as Skia
-            a += 0.375;
-            b += 0.375;
-
-            // Find the normal to the line
-            vec2 n = (b - a).copyNormalized() * strokeWidth;
-            float x = n.x;
-            n.x = -n.y;
-            n.y = x;
-
-            // Four corners of the rectangle defining a thick line
-            vec2 p1 = a - n;
-            vec2 p2 = a + n;
-            vec2 p3 = b + n;
-            vec2 p4 = b - n;
-
-            const float left = fmin(p1.x, fmin(p2.x, fmin(p3.x, p4.x)));
-            const float right = fmax(p1.x, fmax(p2.x, fmax(p3.x, p4.x)));
-            const float top = fmin(p1.y, fmin(p2.y, fmin(p3.y, p4.y)));
-            const float bottom = fmax(p1.y, fmax(p2.y, fmax(p3.y, p4.y)));
-
-            if (!quickReject(left, top, right, bottom)) {
-                // Draw the line as 2 triangles, could be optimized
-                // by using only 4 vertices and the correct indices
-                // Also we should probably used non textured vertices
-                // when line AA is disabled to save on bandwidth
-                TextureVertex::set(vertex++, p1.x, p1.y, 0.0f, 0.0f);
-                TextureVertex::set(vertex++, p2.x, p2.y, 0.0f, 0.0f);
-                TextureVertex::set(vertex++, p3.x, p3.y, 0.0f, 0.0f);
-                TextureVertex::set(vertex++, p1.x, p1.y, 0.0f, 0.0f);
-                TextureVertex::set(vertex++, p3.x, p3.y, 0.0f, 0.0f);
-                TextureVertex::set(vertex++, p4.x, p4.y, 0.0f, 0.0f);
-
-                generatedVerticesCount += 6;
-
-                dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
-            }
-        }
-
-        if (generatedVerticesCount > 0) {
-            // GL_LINE does not give the result we want to match Skia
-            glDrawArrays(GL_TRIANGLES, 0, generatedVerticesCount);
-        }
+        drawLinesAsQuads(points, count, isAA, isHairLine, strokeWidth);
     } else {
-        // TODO: Handle the AA case
-        for (int i = 0; i < count; i += 4) {
-            TextureVertex::set(vertex++, points[i], points[i + 1], 0.0f, 0.0f);
-            TextureVertex::set(vertex++, points[i + 2], points[i + 3], 0.0f, 0.0f);
+        if (isAA) {
+            drawLinesAsQuads(points, count, isAA, isHairLine, .5f);
+        } else {
+            int verticesCount = count >> 1;
+            Vertex lines[verticesCount];
+            Vertex* vertices = &lines[0];
+            setupDrawVertices(vertices);
+            for (int i = 0; i < count; i += 4) {
 
-            generatedVerticesCount += 2;
+                const float left = fmin(points[i], points[i + 2]);
+                const float right = fmax(points[i], points[i + 2]);
+                const float top = fmin(points[i + 1], points[i + 3]);
+                const float bottom = fmax(points[i + 1], points[i + 3]);
 
-            const float left = fmin(points[i], points[i + 2]);
-            const float right = fmax(points[i], points[i + 2]);
-            const float top = fmin(points[i + 1], points[i + 3]);
-            const float bottom = fmax(points[i + 1], points[i + 3]);
+                Vertex::set(vertices++, points[i], points[i + 1]);
+                Vertex::set(vertices++, points[i + 2], points[i + 3]);
+                generatedVerticesCount += 2;
+                dirtyLayer(left, top,
+                        right == left ? left + 1 : right, bottom == top ? top + 1 : bottom,
+                        *mSnapshot->transform);
+            }
 
-            dirtyLayer(left, top,
-                    right == left ? left + 1 : right, bottom == top ? top + 1 : bottom,
-                    *mSnapshot->transform);
+            glLineWidth(1.0f);
+            glDrawArrays(GL_LINES, 0, generatedVerticesCount);
         }
-
-        glLineWidth(1.0f);
-        glDrawArrays(GL_LINES, 0, generatedVerticesCount);
     }
 }
 
@@ -1467,7 +1588,7 @@
     // TODO: The paint's cap style defines whether the points are square or circular
     // TODO: Handle AA for round points
 
-    // A stroke width of 0 has a special meaningin Skia:
+    // A stroke width of 0 has a special meaning in Skia:
     // it draws an unscaled 1px point
     const bool isHairLine = paint->getStrokeWidth() == 0.0f;
 
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 4b93b80..0276095 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -283,6 +283,19 @@
     void drawAlphaBitmap(Texture* texture, float left, float top, SkPaint* paint);
 
     /**
+     * Draws a line as a quad. Called by drawLines() for all cases except hairline without AA.
+     *
+     * @param points The vertices of the lines. Every four entries specifies the x/y points
+     * of a single line segment.
+     * @param count The number of entries in the points array.
+     * @param isAA Whether the line is anti-aliased
+     * @param isHairline Whether the line has strokeWidth==0, which results in the line being
+     * one pixel wide on the display regardless of scale.
+     */
+    void drawLinesAsQuads(float *points, int count, bool isAA, bool isHairline,
+            float strokeWidth);
+
+    /**
      * Draws a textured rectangle with the specified texture. The specified coordinates
      * are transformed by the current snapshot's transform matrix.
      *
@@ -425,6 +438,7 @@
      * Various methods to setup OpenGL rendering.
      */
     void setupDrawWithTexture(bool isAlpha8 = false);
+    void setupDrawAALine();
     void setupDrawPoint(float pointSize);
     void setupDrawColor(int color);
     void setupDrawColor(int color, int alpha);
@@ -453,6 +467,8 @@
     void setupDrawSimpleMesh();
     void setupDrawTexture(GLuint texture);
     void setupDrawMesh(GLvoid* vertices, GLvoid* texCoords = NULL, GLuint vbo = 0);
+    void setupDrawVertices(GLvoid* vertices);
+    void setupDrawAALine(GLvoid* vertices, GLvoid* distanceCoords, float strokeWidth);
     void finishDrawTexture();
 
     void drawRegionRects(const Region& region);
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index f0bc36b..b873bb8 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -39,6 +39,8 @@
         "attribute vec4 position;\n";
 const char* gVS_Header_Attributes_TexCoords =
         "attribute vec2 texCoords;\n";
+const char* gVS_Header_Attributes_Distance =
+        "attribute float vtxDistance;\n";
 const char* gVS_Header_Uniforms =
         "uniform mat4 transform;\n";
 const char* gVS_Header_Uniforms_IsPoint =
@@ -56,6 +58,8 @@
         "uniform mediump vec2 textureDimension;\n";
 const char* gVS_Header_Varyings_HasTexture =
         "varying vec2 outTexCoords;\n";
+const char* gVS_Header_Varyings_HasWidth =
+        "varying float distance;\n";
 const char* gVS_Header_Varyings_HasBitmap =
         "varying vec2 outBitmapTexCoords;\n";
 const char* gVS_Header_Varyings_PointHasBitmap =
@@ -88,6 +92,8 @@
         "    gl_Position = transform * position;\n";
 const char* gVS_Main_PointSize =
         "    gl_PointSize = pointSize;\n";
+const char* gVS_Main_Width =
+        "    distance = vtxDistance;\n";
 const char* gVS_Footer =
         "}\n\n";
 
@@ -101,6 +107,10 @@
         "precision mediump float;\n\n";
 const char* gFS_Uniforms_Color =
         "uniform vec4 color;\n";
+const char* gFS_Uniforms_Width =
+        "uniform float width;\n"
+        "uniform float boundaryWidth;\n"
+        "uniform float inverseBoundaryWidth;\n";
 const char* gFS_Header_Uniforms_PointHasBitmap =
         "uniform vec2 textureDimension;\n"
         "uniform float pointSize;\n";
@@ -169,6 +179,12 @@
 // General case
 const char* gFS_Main_FetchColor =
         "    fragColor = color;\n";
+const char* gFS_Main_AccountForWidth =
+        "    if (distance < boundaryWidth) {\n"
+        "        fragColor *= (distance * inverseBoundaryWidth);\n"
+        "    } else if (distance > (1.0 - boundaryWidth)) {\n"
+        "        fragColor *= ((1.0 - distance) * inverseBoundaryWidth);\n"
+        "    }\n";
 const char* gFS_Main_FetchTexture[2] = {
         // Don't modulate
         "    fragColor = texture2D(sampler, outTexCoords);\n",
@@ -354,6 +370,9 @@
     if (description.hasTexture) {
         shader.append(gVS_Header_Attributes_TexCoords);
     }
+    if (description.hasWidth) {
+        shader.append(gVS_Header_Attributes_Distance);
+    }
     // Uniforms
     shader.append(gVS_Header_Uniforms);
     if (description.hasGradient) {
@@ -369,6 +388,9 @@
     if (description.hasTexture) {
         shader.append(gVS_Header_Varyings_HasTexture);
     }
+    if (description.hasWidth) {
+        shader.append(gVS_Header_Varyings_HasWidth);
+    }
     if (description.hasGradient) {
         shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]);
     }
@@ -383,6 +405,9 @@
         if (description.hasTexture) {
             shader.append(gVS_Main_OutTexCoords);
         }
+        if (description.hasWidth) {
+            shader.append(gVS_Main_Width);
+        }
         if (description.hasGradient) {
             shader.append(gVS_Main_OutGradient[description.gradientType]);
         }
@@ -420,6 +445,9 @@
     if (description.hasTexture) {
         shader.append(gVS_Header_Varyings_HasTexture);
     }
+    if (description.hasWidth) {
+        shader.append(gVS_Header_Varyings_HasWidth);
+    }
     if (description.hasGradient) {
         shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]);
     }
@@ -441,6 +469,9 @@
     if (description.hasTexture) {
         shader.append(gFS_Uniforms_TextureSampler);
     }
+    if (description.hasWidth) {
+        shader.append(gFS_Uniforms_Width);
+    }
     if (description.hasGradient) {
         shader.append(gFS_Uniforms_GradientSampler[description.gradientType]);
     }
@@ -449,8 +480,8 @@
     }
 
     // Optimization for common cases
-    if (!blendFramebuffer && description.colorOp == ProgramDescription::kColorNone &&
-            !description.isPoint) {
+    if (!description.hasWidth && !blendFramebuffer &&
+            description.colorOp == ProgramDescription::kColorNone && !description.isPoint) {
         bool fast = false;
 
         const bool noShader = !description.hasGradient && !description.hasBitmap;
@@ -534,6 +565,9 @@
                 shader.append(gFS_Main_FetchColor);
             }
         }
+        if (description.hasWidth) {
+            shader.append(gFS_Main_AccountForWidth);
+        }
         if (description.hasGradient) {
             shader.append(gFS_Main_FetchGradient[description.gradientType]);
         }
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
index 737d91b..cead75b 100644
--- a/libs/hwui/ProgramCache.h
+++ b/libs/hwui/ProgramCache.h
@@ -57,6 +57,7 @@
 #define PROGRAM_KEY_COLOR_BLEND 0x80
 #define PROGRAM_KEY_BITMAP_NPOT 0x100
 #define PROGRAM_KEY_SWAP_SRC_DST 0x2000
+#define PROGRAM_KEY_VERTEX_WIDTH 0x4000
 
 #define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600
 #define PROGRAM_KEY_BITMAP_WRAPT_MASK 0x1800
@@ -119,6 +120,8 @@
     bool hasBitmap;
     bool isBitmapNpot;
 
+    bool hasWidth;
+
     bool hasGradient;
     Gradient gradientType;
 
@@ -148,6 +151,8 @@
         hasTexture = false;
         hasAlpha8Texture = false;
 
+        hasWidth = false;
+
         modulate = false;
 
         hasBitmap = false;
@@ -200,6 +205,7 @@
     programid key() const {
         programid key = 0;
         if (hasTexture) key |= PROGRAM_KEY_TEXTURE;
+        if (hasWidth) key |= PROGRAM_KEY_VERTEX_WIDTH;
         if (hasAlpha8Texture) key |= PROGRAM_KEY_A8_TEXTURE;
         if (hasBitmap) {
             key |= PROGRAM_KEY_BITMAP;
diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h
index bbf4d4a..c120428 100644
--- a/libs/hwui/Vertex.h
+++ b/libs/hwui/Vertex.h
@@ -23,6 +23,18 @@
 /**
  * Simple structure to describe a vertex with a position and a texture.
  */
+struct Vertex {
+    float position[2];
+
+    static inline void set(Vertex* vertex, float x, float y) {
+        vertex[0].position[0] = x;
+        vertex[0].position[1] = y;
+    }
+}; // struct Vertex
+
+/**
+ * Simple structure to describe a vertex with a position and a texture.
+ */
 struct TextureVertex {
     float position[2];
     float texture[2];
@@ -40,6 +52,22 @@
     }
 }; // struct TextureVertex
 
+/**
+ * Simple structure to describe a vertex with a position and an alpha value.
+ */
+struct AlphaVertex : Vertex {
+    float alpha;
+
+    static inline void set(AlphaVertex* vertex, float x, float y, float alpha) {
+        Vertex::set(vertex, x, y);
+        vertex[0].alpha = alpha;
+    }
+
+    static inline void setColor(AlphaVertex* vertex, float alpha) {
+        vertex[0].alpha = alpha;
+    }
+}; // struct AlphaVertex
+
 }; // namespace uirenderer
 }; // namespace android
 
diff --git a/libs/rs/driver/rsdBcc.cpp b/libs/rs/driver/rsdBcc.cpp
index 2c6840b..8120864 100644
--- a/libs/rs/driver/rsdBcc.cpp
+++ b/libs/rs/driver/rsdBcc.cpp
@@ -56,8 +56,9 @@
 
 };
 
+
 static Script * setTLS(Script *sc) {
-    ScriptTLSStruct * tls = (ScriptTLSStruct *)pthread_getspecific(Context::gThreadTLSKey);
+    ScriptTLSStruct * tls = (ScriptTLSStruct *)pthread_getspecific(rsdgThreadTLSKey);
     rsAssert(tls);
     Script *old = tls->mScript;
     tls->mScript = sc;
@@ -133,12 +134,13 @@
                      uint32_t flags) {
     //LOGE("rsdScriptCreate %p %p %p %p %i %i %p", rsc, resName, cacheDir, bitcode, bitcodeSize, flags, lookupFunc);
 
+    pthread_mutex_lock(&rsdgInitMutex);
     char *cachePath = NULL;
     uint32_t objectSlotCount = 0;
 
     DrvScript *drv = (DrvScript *)calloc(1, sizeof(DrvScript));
     if (drv == NULL) {
-        return false;
+        goto error;
     }
     script->mHal.drv = drv;
 
@@ -159,20 +161,20 @@
                   (char const *)drv->mScriptText,
                   drv->mScriptTextLength, 0) != 0) {
         LOGE("bcc: FAILS to read bitcode");
-        return NULL;
+        goto error;
     }
 
 #if 1
     if (bccLinkFile(drv->mBccScript, "/system/lib/libclcore.bc", 0) != 0) {
         LOGE("bcc: FAILS to link bitcode");
-        return NULL;
+        goto error;
     }
 #endif
     cachePath = genCacheFileName(cacheDir, resName, ".oBCC");
 
     if (bccPrepareExecutable(drv->mBccScript, cachePath, 0) != 0) {
         LOGE("bcc: FAILS to prepare executable");
-        return NULL;
+        goto error;
     }
 
     free(cachePath);
@@ -234,10 +236,12 @@
     script->mHal.info.root = drv->mRoot;
 
 
+    pthread_mutex_unlock(&rsdgInitMutex);
     return true;
 
 error:
 
+    pthread_mutex_unlock(&rsdgInitMutex);
     free(drv);
     return false;
 
diff --git a/libs/rs/driver/rsdCore.cpp b/libs/rs/driver/rsdCore.cpp
index c01e5ab..5b80439 100644
--- a/libs/rs/driver/rsdCore.cpp
+++ b/libs/rs/driver/rsdCore.cpp
@@ -73,6 +73,9 @@
 
 };
 
+pthread_key_t rsdgThreadTLSKey = 0;
+uint32_t rsdgThreadTLSKeyCount = 0;
+pthread_mutex_t rsdgInitMutex = PTHREAD_MUTEX_INITIALIZER;
 
 
 static void * HelperThreadProc(void *vrsc) {
@@ -87,6 +90,11 @@
     dc->mWorkers.mLaunchSignals[idx].init();
     dc->mWorkers.mNativeThreadId[idx] = gettid();
 
+    int status = pthread_setspecific(rsdgThreadTLSKey, &dc->mTlsStruct);
+    if (status) {
+        LOGE("pthread_setspecific %i", status);
+    }
+
 #if 0
     typedef struct {uint64_t bits[1024 / 64]; } cpu_set_t;
     cpu_set_t cpuset;
@@ -97,11 +105,6 @@
     LOGE("SETAFFINITY ret = %i %s", ret, EGLUtils::strerror(ret));
 #endif
 
-    int status = pthread_setspecific(rsc->gThreadTLSKey, rsc->mTlsStruct);
-    if (status) {
-        LOGE("pthread_setspecific %i", status);
-    }
-
     while (!dc->mExit) {
         dc->mWorkers.mLaunchSignals[idx].wait();
         if (dc->mWorkers.mLaunchCallback) {
@@ -139,6 +142,25 @@
     }
     rsc->mHal.drv = dc;
 
+    pthread_mutex_lock(&rsdgInitMutex);
+    if (!rsdgThreadTLSKeyCount) {
+        int status = pthread_key_create(&rsdgThreadTLSKey, NULL);
+        if (status) {
+            LOGE("Failed to init thread tls key.");
+            pthread_mutex_unlock(&rsdgInitMutex);
+            return false;
+        }
+    }
+    rsdgThreadTLSKeyCount++;
+    pthread_mutex_unlock(&rsdgInitMutex);
+
+    dc->mTlsStruct.mContext = rsc;
+    dc->mTlsStruct.mScript = NULL;
+    int status = pthread_setspecific(rsdgThreadTLSKey, &dc->mTlsStruct);
+    if (status) {
+        LOGE("pthread_setspecific %i", status);
+    }
+
 
     int cpu = sysconf(_SC_NPROCESSORS_ONLN);
     LOGV("RS Launching thread(s), reported CPU count %i", cpu);
@@ -155,7 +177,6 @@
     android_atomic_release_store(dc->mWorkers.mCount, &dc->mWorkers.mRunningCount);
     android_atomic_release_store(0, &dc->mWorkers.mLaunchCount);
 
-    int status;
     pthread_attr_t threadAttr;
     status = pthread_attr_init(&threadAttr);
     if (status) {
@@ -203,6 +224,15 @@
         status = pthread_join(dc->mWorkers.mThreadId[ct], &res);
     }
     rsAssert(android_atomic_acquire_load(&dc->mWorkers.mRunningCount) == 0);
+
+    // Global structure cleanup.
+    pthread_mutex_lock(&rsdgInitMutex);
+    --rsdgThreadTLSKeyCount;
+    if (!rsdgThreadTLSKeyCount) {
+        pthread_key_delete(rsdgThreadTLSKey);
+    }
+    pthread_mutex_unlock(&rsdgInitMutex);
+
 }
 
 
diff --git a/libs/rs/driver/rsdCore.h b/libs/rs/driver/rsdCore.h
index c8df575..422bb1b 100644
--- a/libs/rs/driver/rsdCore.h
+++ b/libs/rs/driver/rsdCore.h
@@ -34,6 +34,11 @@
     bool threadable;
 } RsdSymbolTable;
 
+typedef struct ScriptTLSStructRec {
+    android::renderscript::Context * mContext;
+    android::renderscript::Script * mScript;
+} ScriptTLSStruct;
+
 typedef struct RsdHalRec {
     uint32_t version_major;
     uint32_t version_minor;
@@ -53,9 +58,14 @@
     Workers mWorkers;
     bool mExit;
 
+    ScriptTLSStruct mTlsStruct;
+
     RsdGL gl;
 } RsdHal;
 
+extern pthread_key_t rsdgThreadTLSKey;
+extern uint32_t rsdgThreadTLSKeyCount;
+extern pthread_mutex_t rsdgInitMutex;
 
 
 void rsdLaunchThreads(android::renderscript::Context *rsc, WorkerCallback_t cbk, void *data);
diff --git a/libs/rs/driver/rsdRuntimeStubs.cpp b/libs/rs/driver/rsdRuntimeStubs.cpp
index e384713..b70a123 100644
--- a/libs/rs/driver/rsdRuntimeStubs.cpp
+++ b/libs/rs/driver/rsdRuntimeStubs.cpp
@@ -32,7 +32,7 @@
 using namespace android::renderscript;
 
 #define GET_TLS()  ScriptTLSStruct * tls = \
-    (ScriptTLSStruct *)pthread_getspecific(Context::gThreadTLSKey); \
+    (ScriptTLSStruct *)pthread_getspecific(rsdgThreadTLSKey); \
     Context * rsc = tls->mContext; \
     ScriptC * sc = (ScriptC *) tls->mScript
 
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index 0f61789..20fa367 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -39,13 +39,9 @@
 using namespace android;
 using namespace android::renderscript;
 
-pthread_key_t Context::gThreadTLSKey = 0;
-uint32_t Context::gThreadTLSKeyCount = 0;
 pthread_mutex_t Context::gInitMutex = PTHREAD_MUTEX_INITIALIZER;
 pthread_mutex_t Context::gLibMutex = PTHREAD_MUTEX_INITIALIZER;
 
-
-
 bool Context::initGLThread() {
     pthread_mutex_lock(&gInitMutex);
     LOGV("initGLThread start %p", this);
@@ -263,94 +259,86 @@
 }
 
 void * Context::threadProc(void *vrsc) {
-     Context *rsc = static_cast<Context *>(vrsc);
-     rsc->mNativeThreadId = gettid();
+    Context *rsc = static_cast<Context *>(vrsc);
+    rsc->mNativeThreadId = gettid();
 
-     setpriority(PRIO_PROCESS, rsc->mNativeThreadId, ANDROID_PRIORITY_DISPLAY);
-     rsc->mThreadPriority = ANDROID_PRIORITY_DISPLAY;
+    setpriority(PRIO_PROCESS, rsc->mNativeThreadId, ANDROID_PRIORITY_DISPLAY);
+    rsc->mThreadPriority = ANDROID_PRIORITY_DISPLAY;
 
-     rsc->props.mLogTimes = getProp("debug.rs.profile");
-     rsc->props.mLogScripts = getProp("debug.rs.script");
-     rsc->props.mLogObjects = getProp("debug.rs.object");
-     rsc->props.mLogShaders = getProp("debug.rs.shader");
-     rsc->props.mLogShadersAttr = getProp("debug.rs.shader.attributes");
-     rsc->props.mLogShadersUniforms = getProp("debug.rs.shader.uniforms");
-     rsc->props.mLogVisual = getProp("debug.rs.visual");
+    rsc->props.mLogTimes = getProp("debug.rs.profile");
+    rsc->props.mLogScripts = getProp("debug.rs.script");
+    rsc->props.mLogObjects = getProp("debug.rs.object");
+    rsc->props.mLogShaders = getProp("debug.rs.shader");
+    rsc->props.mLogShadersAttr = getProp("debug.rs.shader.attributes");
+    rsc->props.mLogShadersUniforms = getProp("debug.rs.shader.uniforms");
+    rsc->props.mLogVisual = getProp("debug.rs.visual");
 
-     rsc->mTlsStruct = new ScriptTLSStruct;
-     if (!rsc->mTlsStruct) {
-         LOGE("Error allocating tls storage");
-         rsc->setError(RS_ERROR_OUT_OF_MEMORY, "Failed allocation for TLS");
-         return NULL;
-     }
-     rsc->mTlsStruct->mContext = rsc;
-     rsc->mTlsStruct->mScript = NULL;
-     int status = pthread_setspecific(rsc->gThreadTLSKey, rsc->mTlsStruct);
-     if (status) {
-         LOGE("pthread_setspecific %i", status);
-     }
+    if (!rsdHalInit(rsc, 0, 0)) {
+        LOGE("Hal init failed");
+        return NULL;
+    }
+    rsc->mHal.funcs.setPriority(rsc, rsc->mThreadPriority);
 
-     if (!rsc->initGLThread()) {
-         rsc->setError(RS_ERROR_OUT_OF_MEMORY, "Failed initializing GL");
-         return NULL;
-     }
+    if (!rsc->initGLThread()) {
+        rsc->setError(RS_ERROR_OUT_OF_MEMORY, "Failed initializing GL");
+        return NULL;
+    }
 
-     if (rsc->mIsGraphicsContext) {
-         rsc->mStateRaster.init(rsc);
-         rsc->setProgramRaster(NULL);
-         rsc->mStateVertex.init(rsc);
-         rsc->setProgramVertex(NULL);
-         rsc->mStateFragment.init(rsc);
-         rsc->setProgramFragment(NULL);
-         rsc->mStateFragmentStore.init(rsc);
-         rsc->setProgramStore(NULL);
-         rsc->mStateFont.init(rsc);
-         rsc->setFont(NULL);
-         rsc->mStateVertexArray.init(rsc);
-     }
+    if (rsc->mIsGraphicsContext) {
+        rsc->mStateRaster.init(rsc);
+        rsc->setProgramRaster(NULL);
+        rsc->mStateVertex.init(rsc);
+        rsc->setProgramVertex(NULL);
+        rsc->mStateFragment.init(rsc);
+        rsc->setProgramFragment(NULL);
+        rsc->mStateFragmentStore.init(rsc);
+        rsc->setProgramStore(NULL);
+        rsc->mStateFont.init(rsc);
+        rsc->setFont(NULL);
+        rsc->mStateVertexArray.init(rsc);
+    }
 
-     rsc->mRunning = true;
-     bool mDraw = true;
-     while (!rsc->mExit) {
-         mDraw |= rsc->mIO.playCoreCommands(rsc, !mDraw);
-         mDraw &= (rsc->mRootScript.get() != NULL);
-         mDraw &= (rsc->mWndSurface != NULL);
+    rsc->mRunning = true;
+    bool mDraw = true;
+    while (!rsc->mExit) {
+        mDraw |= rsc->mIO.playCoreCommands(rsc, !mDraw);
+        mDraw &= (rsc->mRootScript.get() != NULL);
+        mDraw &= (rsc->mWndSurface != NULL);
 
-         uint32_t targetTime = 0;
-         if (mDraw && rsc->mIsGraphicsContext) {
-             targetTime = rsc->runRootScript();
+        uint32_t targetTime = 0;
+        if (mDraw && rsc->mIsGraphicsContext) {
+            targetTime = rsc->runRootScript();
 
-             if (rsc->props.mLogVisual) {
-                 rsc->displayDebugStats();
-             }
+            if (rsc->props.mLogVisual) {
+                rsc->displayDebugStats();
+            }
 
-             mDraw = targetTime && !rsc->mPaused;
-             rsc->timerSet(RS_TIMER_CLEAR_SWAP);
-             rsc->mHal.funcs.swap(rsc);
-             rsc->timerFrame();
-             rsc->timerSet(RS_TIMER_INTERNAL);
-             rsc->timerPrint();
-             rsc->timerReset();
-         }
-         if (targetTime > 1) {
-             int32_t t = (targetTime - (int32_t)(rsc->mTimeMSLastScript + rsc->mTimeMSLastSwap)) * 1000;
-             if (t > 0) {
-                 usleep(t);
-             }
-         }
-     }
+            mDraw = targetTime && !rsc->mPaused;
+            rsc->timerSet(RS_TIMER_CLEAR_SWAP);
+            rsc->mHal.funcs.swap(rsc);
+            rsc->timerFrame();
+            rsc->timerSet(RS_TIMER_INTERNAL);
+            rsc->timerPrint();
+            rsc->timerReset();
+        }
+        if (targetTime > 1) {
+            int32_t t = (targetTime - (int32_t)(rsc->mTimeMSLastScript + rsc->mTimeMSLastSwap)) * 1000;
+            if (t > 0) {
+                usleep(t);
+            }
+        }
+    }
 
-     LOGV("%p, RS Thread exiting", rsc);
+    LOGV("%p, RS Thread exiting", rsc);
 
-     if (rsc->mIsGraphicsContext) {
-         pthread_mutex_lock(&gInitMutex);
-         rsc->deinitEGL();
-         pthread_mutex_unlock(&gInitMutex);
-     }
-     delete rsc->mTlsStruct;
+    if (rsc->mIsGraphicsContext) {
+        pthread_mutex_lock(&gInitMutex);
+        rsc->deinitEGL();
+        pthread_mutex_unlock(&gInitMutex);
+    }
 
-     LOGV("%p, RS Thread exited", rsc);
-     return NULL;
+    LOGV("%p, RS Thread exited", rsc);
+    return NULL;
 }
 
 void Context::destroyWorkerThreadResources() {
@@ -429,16 +417,6 @@
     int status;
     pthread_attr_t threadAttr;
 
-    if (!gThreadTLSKeyCount) {
-        status = pthread_key_create(&gThreadTLSKey, NULL);
-        if (status) {
-            LOGE("Failed to init thread tls key.");
-            pthread_mutex_unlock(&gInitMutex);
-            return false;
-        }
-    }
-    gThreadTLSKeyCount++;
-
     pthread_mutex_unlock(&gInitMutex);
 
     // Global init done at this point.
@@ -454,12 +432,6 @@
     timerInit();
     timerSet(RS_TIMER_INTERNAL);
 
-    if (!rsdHalInit(this, 0, 0)) {
-        LOGE("Hal init failed");
-        return false;
-    }
-    mHal.funcs.setPriority(this, mThreadPriority);
-
     status = pthread_create(&mThreadId, &threadAttr, threadProc, this);
     if (status) {
         LOGE("Failed to start rs context thread.");
@@ -499,10 +471,6 @@
     pthread_mutex_lock(&gInitMutex);
     if (mDev) {
         mDev->removeContext(this);
-        --gThreadTLSKeyCount;
-        if (!gThreadTLSKeyCount) {
-            pthread_key_delete(gThreadTLSKey);
-        }
         mDev = NULL;
     }
     pthread_mutex_unlock(&gInitMutex);
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
index 0a0f3e0..df85a6b 100644
--- a/libs/rs/rsContext.h
+++ b/libs/rs/rsContext.h
@@ -84,9 +84,6 @@
     static Context * createContext(Device *, const RsSurfaceConfig *sc);
     ~Context();
 
-    static pthread_key_t gThreadTLSKey;
-    static uint32_t gThreadTLSKeyCount;
-    static uint32_t gGLContextCount;
     static pthread_mutex_t gInitMutex;
     // Library mutex (for providing thread-safe calls from the runtime)
     static pthread_mutex_t gLibMutex;
@@ -105,7 +102,6 @@
         Context *mRsc;
     };
 
-    ScriptTLSStruct *mTlsStruct;
     RsSurfaceConfig mUserSurfaceConfig;
 
     ElementState mStateElement;
diff --git a/libs/rs/rs_hal.h b/libs/rs/rs_hal.h
index 90abd85..4283d4a 100644
--- a/libs/rs/rs_hal.h
+++ b/libs/rs/rs_hal.h
@@ -35,12 +35,6 @@
 
 typedef void *(*RsHalSymbolLookupFunc)(void *usrptr, char const *symbolName);
 
-typedef struct ScriptTLSStructRec {
-    Context * mContext;
-    Script * mScript;
-} ScriptTLSStruct;
-
-
 /**
  * Script management functions
  */
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 51e715d..a7ac9ed 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -1176,8 +1176,7 @@
             mGenresUri = Genres.getContentUri(volumeName);
             mPlaylistsUri = Playlists.getContentUri(volumeName);
 
-            mCaseInsensitivePaths = !mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_caseSensitiveExternalStorage);
+            mCaseInsensitivePaths = true;
             if (!Process.supportsProcesses()) {
                 // Simulator uses host file system, so it should be case sensitive.
                 mCaseInsensitivePaths = false;
diff --git a/opengl/java/android/opengl/Matrix.java b/opengl/java/android/opengl/Matrix.java
index b9fd4ab..6d80bc6 100644
--- a/opengl/java/android/opengl/Matrix.java
+++ b/opengl/java/android/opengl/Matrix.java
@@ -16,8 +16,6 @@
 
 package android.opengl;
 
-import javax.microedition.khronos.opengles.GL10;
-
 /**
  * Matrix math utilities. These methods operate on OpenGL ES format
  * matrices and vectors stored in float arrays.
@@ -332,6 +330,43 @@
     }
 
     /**
+     * Define a projection matrix in terms of a field of view angle, an
+     * aspect ratio, and z clip planes
+     * @param m the float array that holds the perspective matrix
+     * @param offset the offset into float array m where the perspective
+     * matrix data is written
+     * @param fovy field of view in y direction, in degrees
+     * @param aspect width to height aspect ratio of the viewport
+     * @param zNear
+     * @param zFar
+     */
+    public static void perspectiveM(float[] m, int offset,
+          float fovy, float aspect, float zNear, float zFar) {
+        float f = 1.0f / (float) Math.tan(fovy * (Math.PI / 360.0));
+        float rangeReciprocal = 1.0f / (zNear - zFar);
+
+        m[offset + 0] = f / aspect;
+        m[offset + 1] = 0.0f;
+        m[offset + 2] = 0.0f;
+        m[offset + 3] = 0.0f;
+
+        m[offset + 4] = 0.0f;
+        m[offset + 5] = f;
+        m[offset + 6] = 0.0f;
+        m[offset + 7] = 0.0f;
+
+        m[offset + 8] = 0.0f;
+        m[offset + 9] = 0.0f;
+        m[offset + 10] = (zFar + zNear) * rangeReciprocal;
+        m[offset + 11] = -1.0f;
+
+        m[offset + 12] = 0.0f;
+        m[offset + 13] = 0.0f;
+        m[offset + 14] = 2.0f * zFar * zNear * rangeReciprocal;
+        m[offset + 15] = 0.0f;
+    }
+
+    /**
      * Computes the length of a vector
      *
      * @param x x coordinate of a vector
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 2afc935..c763b1d 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -388,6 +388,15 @@
         </activity>
 
         <activity
+                android:name="Lines2Activity"
+                android:label="_Lines2">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity
                 android:name="PathsActivity"
                 android:label="_Paths">
             <intent-filter>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/Lines2Activity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/Lines2Activity.java
new file mode 100644
index 0000000..ccf0631
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/Lines2Activity.java
@@ -0,0 +1,149 @@
+/*
+ * 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.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.drawable.ColorDrawable;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.FrameLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class Lines2Activity extends Activity {
+    private ObjectAnimator mAnimator;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        getWindow().setBackgroundDrawable(new ColorDrawable(0xff000000));
+        FrameLayout frame = new FrameLayout(this);
+        final LinesView gpuView = new LinesView(this, 0, Color.GREEN);
+        frame.addView(gpuView);
+        final LinesView swView = new LinesView(this, 400, Color.RED);
+        swView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+        frame.addView(swView);
+        final LinesView hwBothView = new LinesView(this, 850, Color.GREEN);
+        // BUG: some lines not drawn or drawn with alpha when enabling hw layers
+//        hwBothView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+        frame.addView(hwBothView);
+        final LinesView swBothView = new LinesView(this, 854, Color.RED);
+        swBothView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+        frame.addView(swBothView);
+        setContentView(frame);
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+    }
+
+    public static class LinesView extends View {
+
+        private float mOffset;
+        private int mColor;
+        private float[] basePoints = {
+                120, 0, 120, 20, 120, 20, 125, 0, 130, 0, 132, 10
+        };
+        private float[] copyPoints = new float[12];
+
+        public LinesView(Context c, float offset, int color) {
+            super(c);
+            mOffset = offset;
+            mColor = color;
+        }
+
+        private void drawLines(Canvas canvas, Paint p, float xOffset, float yOffset) {
+            canvas.drawLine(10 + xOffset, yOffset, 10 + xOffset, 40 + yOffset, p);
+            canvas.drawLine(30 + xOffset, yOffset, 40 + xOffset, 40 + yOffset, p);
+            canvas.drawLine(40 + xOffset, yOffset, 75 + xOffset, 35 + yOffset, p);
+            canvas.drawLine(50 + xOffset, 5+ yOffset, 100 + xOffset, 15 + yOffset, p);
+            canvas.drawLine(60 + xOffset, yOffset, 110 + xOffset, 2 + yOffset, p);
+            canvas.drawLine(60 + xOffset, 40 + yOffset, 110 + xOffset, 40 + yOffset, p);
+            for (int i = 0; i < 12; i += 2) {
+                copyPoints[i] = basePoints[i] + xOffset;
+                copyPoints[i+1] = basePoints[i+1] + yOffset;
+            }
+            canvas.drawLines(copyPoints, 0, 12, p);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            Paint p = new Paint();
+            p.setColor(mColor);
+            float yOffset = 10;
+
+            canvas.save();
+            drawLines(canvas, p, mOffset, yOffset);
+            canvas.scale(2, 2);
+            canvas.translate(60, 0);
+            drawLines(canvas, p, mOffset/2, yOffset/2);
+            canvas.restore();
+
+            yOffset +=100;
+            canvas.save();
+            p.setStrokeWidth(1);
+            drawLines(canvas, p, mOffset, yOffset);
+            canvas.scale(2, 2);
+            canvas.translate(60, 0);
+            drawLines(canvas, p, mOffset/2, yOffset/2);
+            canvas.restore();
+
+            yOffset += 100;
+            canvas.save();
+            p.setStrokeWidth(2);
+            drawLines(canvas, p, mOffset, yOffset);
+            canvas.scale(2, 2);
+            canvas.translate(60, 0);
+            drawLines(canvas, p, mOffset/2, yOffset/2);
+            canvas.restore();
+
+            p.setAntiAlias(true);
+            p.setStrokeWidth(0);
+            yOffset += 100;
+            canvas.save();
+            drawLines(canvas, p, mOffset, yOffset);
+            canvas.scale(2, 2);
+            canvas.translate(60, 0);
+            drawLines(canvas, p, mOffset/2, yOffset/2);
+            canvas.restore();
+
+            yOffset += 100;
+            canvas.save();
+            p.setStrokeWidth(1);
+            drawLines(canvas, p, mOffset, yOffset);
+            canvas.scale(2, 2);
+            canvas.translate(60, 0);
+            drawLines(canvas, p, mOffset/2, yOffset/2);
+            canvas.restore();
+
+            yOffset += 100;
+            canvas.save();
+            p.setStrokeWidth(2);
+            drawLines(canvas, p, mOffset, yOffset);
+            canvas.scale(2, 2);
+            canvas.translate(60, 0);
+            drawLines(canvas, p, mOffset/2, yOffset/2);
+            canvas.restore();
+        }
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java
index 4233367..c3a91ce 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java
@@ -53,7 +53,7 @@
     }
 
     public static class LinesView extends View {
-        private static final boolean LINE_AA = false;
+        private static final boolean LINE_AA = true;
 
         private final Bitmap mBitmap1;
         private final Paint mSmallPaint;