Merge "Updated math_agree test"
diff --git a/api/current.txt b/api/current.txt
index 29e468b..fc06774 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22826,6 +22826,17 @@
     method public void set(T, V);
   }
 
+  public class PropertyValueModel extends android.util.ValueModel {
+    method public T get();
+    method public H getHost();
+    method public android.util.Property<H, T> getProperty();
+    method public java.lang.Class<T> getType();
+    method public static android.util.PropertyValueModel<H, T> of(H, android.util.Property<H, T>);
+    method public static android.util.PropertyValueModel<H, T> of(H, java.lang.Class<T>, java.lang.String);
+    method public static android.util.PropertyValueModel of(java.lang.Object, java.lang.String);
+    method public void set(T);
+  }
+
   public class SparseArray implements java.lang.Cloneable {
     ctor public SparseArray();
     ctor public SparseArray(int);
@@ -22974,6 +22985,14 @@
     field public int type;
   }
 
+  public abstract class ValueModel {
+    ctor protected ValueModel();
+    method public abstract T get();
+    method public abstract java.lang.Class<T> getType();
+    method public abstract void set(T);
+    field public static final android.util.ValueModel EMPTY;
+  }
+
   public class Xml {
     method public static android.util.AttributeSet asAttributeSet(org.xmlpull.v1.XmlPullParser);
     method public static android.util.Xml.Encoding findEncodingByName(java.lang.String) throws java.io.UnsupportedEncodingException;
@@ -27395,10 +27414,12 @@
     method public abstract void onSelectedDayChange(android.widget.CalendarView, int, int, int);
   }
 
-  public class CheckBox extends android.widget.CompoundButton {
+  public class CheckBox extends android.widget.CompoundButton implements android.widget.ValueEditor {
     ctor public CheckBox(android.content.Context);
     ctor public CheckBox(android.content.Context, android.util.AttributeSet);
     ctor public CheckBox(android.content.Context, android.util.AttributeSet, int);
+    method public android.util.ValueModel<java.lang.Boolean> getValueModel();
+    method public void setValueModel(android.util.ValueModel<java.lang.Boolean>);
   }
 
   public abstract interface Checkable {
@@ -27571,14 +27592,16 @@
     method public void setSize(int, int);
   }
 
-  public class EditText extends android.widget.TextView {
+  public class EditText extends android.widget.TextView implements android.widget.ValueEditor {
     ctor public EditText(android.content.Context);
     ctor public EditText(android.content.Context, android.util.AttributeSet);
     ctor public EditText(android.content.Context, android.util.AttributeSet, int);
     method public void extendSelection(int);
+    method public android.util.ValueModel<java.lang.CharSequence> getValueModel();
     method public void selectAll();
     method public void setSelection(int, int);
     method public void setSelection(int);
+    method public void setValueModel(android.util.ValueModel<java.lang.CharSequence>);
   }
 
   public abstract interface ExpandableListAdapter {
@@ -28595,11 +28618,13 @@
     method public abstract java.lang.Object[] getSections();
   }
 
-  public class SeekBar extends android.widget.AbsSeekBar {
+  public class SeekBar extends android.widget.AbsSeekBar implements android.widget.ValueEditor {
     ctor public SeekBar(android.content.Context);
     ctor public SeekBar(android.content.Context, android.util.AttributeSet);
     ctor public SeekBar(android.content.Context, android.util.AttributeSet, int);
+    method public android.util.ValueModel<java.lang.Integer> getValueModel();
     method public void setOnSeekBarChangeListener(android.widget.SeekBar.OnSeekBarChangeListener);
+    method public void setValueModel(android.util.ValueModel<java.lang.Integer>);
   }
 
   public static abstract interface SeekBar.OnSeekBarChangeListener {
@@ -29171,6 +29196,11 @@
     method public android.widget.TextView getText2();
   }
 
+  public abstract interface ValueEditor {
+    method public abstract android.util.ValueModel<T> getValueModel();
+    method public abstract void setValueModel(android.util.ValueModel<T>);
+  }
+
   public class VideoView extends android.view.SurfaceView implements android.widget.MediaController.MediaPlayerControl {
     ctor public VideoView(android.content.Context);
     ctor public VideoView(android.content.Context, android.util.AttributeSet);
diff --git a/core/java/android/util/PropertyValueModel.java b/core/java/android/util/PropertyValueModel.java
new file mode 100755
index 0000000..eb9c47d
--- /dev/null
+++ b/core/java/android/util/PropertyValueModel.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+/**
+ * A value model for a {@link Property property} of a host object. This class can be used for
+ * both reflective and non-reflective property implementations.
+ *
+ * @param <H> the host type, where the host is the object that holds this property
+ * @param <T> the value type
+ *
+ * @see Property
+ * @see ValueModel
+ */
+public class PropertyValueModel<H, T> extends ValueModel<T> {
+    private final H mHost;
+    private final Property<H, T> mProperty;
+
+    private PropertyValueModel(H host, Property<H, T> property) {
+        mProperty = property;
+        mHost = host;
+    }
+
+    /**
+     * Returns the host.
+     *
+     * @return the host
+     */
+    public H getHost() {
+        return mHost;
+    }
+
+    /**
+     * Returns the property.
+     *
+     * @return the property
+     */
+    public Property<H, T> getProperty() {
+        return mProperty;
+    }
+
+    @Override
+    public Class<T> getType() {
+        return mProperty.getType();
+    }
+
+    @Override
+    public T get() {
+        return mProperty.get(mHost);
+    }
+
+    @Override
+    public void set(T value) {
+        mProperty.set(mHost, value);
+    }
+
+    /**
+     * Return an appropriate PropertyValueModel for this host and property.
+     *
+     * @param host the host
+     * @param property the property
+     * @return the value model
+     */
+    public static <H, T> PropertyValueModel<H, T> of(H host, Property<H, T> property) {
+        return new PropertyValueModel<H, T>(host, property);
+    }
+
+    /**
+     * Return a PropertyValueModel for this {@code host} and a
+     * reflective property, constructed from this {@code propertyType} and {@code propertyName}.
+     *
+     * @param host
+     * @param propertyType the property type
+     * @param propertyName the property name
+     * @return a value model with this host and a reflective property with this type and name
+     *
+     * @see Property#of
+     */
+    public static <H, T> PropertyValueModel<H, T> of(H host, Class<T> propertyType,
+            String propertyName) {
+        return of(host, Property.of((Class<H>) host.getClass(), propertyType, propertyName));
+    }
+
+    private static Class getNullaryMethodReturnType(Class c, String name) {
+        try {
+            return c.getMethod(name).getReturnType();
+        } catch (NoSuchMethodException e) {
+            return null;
+        }
+    }
+
+    private static Class getFieldType(Class c, String name) {
+        try {
+            return c.getField(name).getType();
+        } catch (NoSuchFieldException e) {
+            return null;
+        }
+    }
+
+    private static String capitalize(String name) {
+        if (name.isEmpty()) {
+            return name;
+        }
+        return Character.toUpperCase(name.charAt(0)) + name.substring(1);
+    }
+
+    /**
+     * Return a PropertyValueModel for this {@code host} and and {@code propertyName}.
+     *
+     * @param host the host
+     * @param propertyName the property name
+     * @return a value model with this host and a reflective property with this name
+     */
+    public static PropertyValueModel of(Object host, String propertyName) {
+        Class clazz = host.getClass();
+        String suffix = capitalize(propertyName);
+        Class propertyType = getNullaryMethodReturnType(clazz, "get" + suffix);
+        if (propertyType == null) {
+            propertyType = getNullaryMethodReturnType(clazz, "is" + suffix);
+        } 
+        if (propertyType == null) {
+            propertyType = getFieldType(clazz, propertyName); 
+        }         
+        if (propertyType == null) {
+            throw new NoSuchPropertyException(propertyName); 
+        }
+        return of(host, propertyType, propertyName);
+    }
+}
diff --git a/core/java/android/util/ValueModel.java b/core/java/android/util/ValueModel.java
new file mode 100755
index 0000000..4789682
--- /dev/null
+++ b/core/java/android/util/ValueModel.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+/**
+ * A ValueModel is an abstraction for a 'slot' or place in memory in which a value
+ * may be stored and retrieved. A common implementation of ValueModel is a regular property of
+ * an object, whose value may be retrieved by calling the appropriate <em>getter</em>
+ * method and set by calling the corresponding <em>setter</em> method.
+ *
+ * @param <T> the value type
+ *
+ * @see PropertyValueModel
+ */
+public abstract class ValueModel<T> {
+    /**
+     * The empty model should be used in place of {@code null} to indicate that a
+     * model has not been set. The empty model has no value and does nothing when it is set.
+     */
+    public static final ValueModel EMPTY = new ValueModel() {
+        @Override
+        public Class getType() {
+            return Object.class;
+        }
+
+        @Override
+        public Object get() {
+            return null;
+        }
+
+        @Override
+        public void set(Object value) {
+
+        }
+    };
+
+    protected ValueModel() {
+    }
+
+    /**
+     * Returns the type of this property.
+     *
+     * @return the property type
+     */
+    public abstract Class<T> getType();
+
+    /**
+     * Returns the value of this property.
+     *
+     * @return the property value
+     */
+    public abstract T get();
+
+    /**
+     * Sets the value of this property.
+     *
+     * @param value the new value for this property
+     */
+    public abstract void set(T value);
+}
\ No newline at end of file
diff --git a/core/java/android/widget/CheckBox.java b/core/java/android/widget/CheckBox.java
index 858c415..6d292dc 100644
--- a/core/java/android/widget/CheckBox.java
+++ b/core/java/android/widget/CheckBox.java
@@ -20,6 +20,7 @@
 import android.util.AttributeSet;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.util.ValueModel;
 
 
 /**
@@ -55,7 +56,9 @@
  * {@link android.R.styleable#View View Attributes}
  * </p>
  */
-public class CheckBox extends CompoundButton {
+public class CheckBox extends CompoundButton implements ValueEditor<Boolean> {
+    private ValueModel<Boolean> mValueModel = ValueModel.EMPTY;
+
     public CheckBox(Context context) {
         this(context, null);
     }
@@ -79,4 +82,22 @@
         super.onInitializeAccessibilityNodeInfo(info);
         info.setClassName(CheckBox.class.getName());
     }
+
+    @Override
+    public ValueModel<Boolean> getValueModel() {
+        return mValueModel;
+    }
+
+    @Override
+    public void setValueModel(ValueModel<Boolean> valueModel) {
+        mValueModel = valueModel;
+        setChecked(mValueModel.get());
+    }
+
+    @Override
+    public boolean performClick() {
+        boolean handled = super.performClick();
+        mValueModel.set(isChecked());
+        return handled;
+    }
 }
diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java
index 2fd8768..8686177 100644
--- a/core/java/android/widget/EditText.java
+++ b/core/java/android/widget/EditText.java
@@ -17,6 +17,7 @@
 package android.widget;
 
 import android.content.Context;
+import android.graphics.Rect;
 import android.text.Editable;
 import android.text.Selection;
 import android.text.Spannable;
@@ -24,6 +25,7 @@
 import android.text.method.ArrowKeyMovementMethod;
 import android.text.method.MovementMethod;
 import android.util.AttributeSet;
+import android.util.ValueModel;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 
@@ -47,7 +49,9 @@
  * {@link android.R.styleable#TextView TextView Attributes},
  * {@link android.R.styleable#View View Attributes}
  */
-public class EditText extends TextView {
+public class EditText extends TextView implements ValueEditor<CharSequence> {
+    private ValueModel<CharSequence> mValueModel = ValueModel.EMPTY;
+
     public EditText(Context context) {
         this(context, null);
     }
@@ -128,4 +132,21 @@
         super.onInitializeAccessibilityNodeInfo(info);
         info.setClassName(EditText.class.getName());
     }
+
+    @Override
+    public ValueModel<CharSequence> getValueModel() {
+        return mValueModel;
+    }
+
+    @Override
+    public void setValueModel(ValueModel<CharSequence> valueModel) {
+        mValueModel = valueModel;
+        setText(mValueModel.get());
+    }
+
+    @Override
+    void sendAfterTextChanged(Editable text) {
+        super.sendAfterTextChanged(text);
+        mValueModel.set(text);
+    }
 }
diff --git a/core/java/android/widget/SeekBar.java b/core/java/android/widget/SeekBar.java
index 2737f94..a6486a8 100644
--- a/core/java/android/widget/SeekBar.java
+++ b/core/java/android/widget/SeekBar.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
+import android.util.ValueModel;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 
@@ -33,7 +34,7 @@
  *
  * @attr ref android.R.styleable#SeekBar_thumb
  */
-public class SeekBar extends AbsSeekBar {
+public class SeekBar extends AbsSeekBar implements ValueEditor<Integer> {
 
     /**
      * A callback that notifies clients when the progress level has been
@@ -69,8 +70,9 @@
         void onStopTrackingTouch(SeekBar seekBar);
     }
 
+    private ValueModel<Integer> mValueModel = ValueModel.EMPTY;
     private OnSeekBarChangeListener mOnSeekBarChangeListener;
-    
+
     public SeekBar(Context context) {
         this(context, null);
     }
@@ -89,9 +91,23 @@
 
         if (mOnSeekBarChangeListener != null) {
             mOnSeekBarChangeListener.onProgressChanged(this, getProgress(), fromUser);
+            if (fromUser) {
+                mValueModel.set(getProgress());
+            }
         }
     }
 
+    @Override
+    public ValueModel<Integer> getValueModel() {
+        return mValueModel;
+    }
+
+    @Override
+    public void setValueModel(ValueModel<Integer> valueModel) {
+        mValueModel = valueModel;
+        setProgress(mValueModel.get());
+    }
+
     /**
      * Sets a listener to receive notifications of changes to the SeekBar's progress level. Also
      * provides notifications of when the user starts and stops a touch gesture within the SeekBar.
diff --git a/core/java/android/widget/ValueEditor.java b/core/java/android/widget/ValueEditor.java
new file mode 100755
index 0000000..2b91abf
--- /dev/null
+++ b/core/java/android/widget/ValueEditor.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.util.ValueModel;
+
+/**
+ * An interface for editors of simple values. Classes implementing this interface are normally
+ * UI controls (subclasses of {@link android.view.View View}) that can provide a suitable
+ * user interface to display and edit values of the specified type. This interface is
+ * intended to describe editors for simple types, like {@code boolean}, {@code int} or
+ * {@code String}, where the values themselves are immutable.
+ * <p>
+ * For example, {@link android.widget.CheckBox CheckBox} implements
+ * this interface for the Boolean type as it is capable of providing an appropriate
+ * mechanism for displaying and changing the value of a Boolean property.
+ *
+ * @param <T> the value type that this editor supports
+ */
+public interface ValueEditor<T> {
+    /**
+     * Return the last value model that was set. If no value model has been set, the editor
+     * should return the value {@link android.util.ValueModel#EMPTY}.
+     *
+     * @return the value model
+     */
+    public ValueModel<T> getValueModel();
+
+    /**
+     * Sets the value model for this editor. When the value model is set, the editor should
+     * retrieve the value from the value model, using {@link android.util.ValueModel#get()},
+     * and set its internal state accordingly. Likewise, when the editor's internal state changes
+     * it should update the value model by calling  {@link android.util.ValueModel#set(T)}
+     * with the appropriate value.
+     *
+     * @param valueModel the new value model for this editor.
+     */
+    public void setValueModel(ValueModel<T> valueModel);
+}
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index eeb37cb..7f0ed73 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -288,13 +288,13 @@
 }
 
 void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
-        int numGlyphs, Rect *bounds) {
+        int numGlyphs, Rect *bounds, const float* positions) {
     if (bounds == NULL) {
         ALOGE("No return rectangle provided to measure text");
         return;
     }
     bounds->set(1e6, -1e6, -1e6, 1e6);
-    render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, NULL);
+    render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions);
 }
 
 void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
@@ -1007,7 +1007,7 @@
 }
 
 FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
-        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
+        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
     checkInit();
 
     if (!mCurrentFont) {
@@ -1025,7 +1025,7 @@
     mBounds = NULL;
 
     Rect bounds;
-    mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds);
+    mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
 
     uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
     uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
@@ -1039,7 +1039,7 @@
     int penY = radius - bounds.bottom;
 
     mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
-            dataBuffer, paddedWidth, paddedHeight);
+            Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
     blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
 
     DropShadow image;
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index b7b4ec9..9ed6932 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -184,7 +184,7 @@
             uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions);
 
     void measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
-            int numGlyphs, Rect *bounds);
+            int numGlyphs, Rect *bounds, const float* positions);
 
     Font(FontRenderer* state, uint32_t fontId, float fontSize, int flags, uint32_t italicStyle,
             uint32_t scaleX, SkPaint::Style style, uint32_t strokeWidth);
@@ -273,7 +273,7 @@
     // After renderDropShadow returns, the called owns the memory in DropShadow.image
     // and is responsible for releasing it when it's done with it
     DropShadow renderDropShadow(SkPaint* paint, const char *text, uint32_t startIndex,
-            uint32_t len, int numGlyphs, uint32_t radius);
+            uint32_t len, int numGlyphs, uint32_t radius, const float* positions);
 
     GLuint getTexture(bool linearFiltering = false) {
         checkInit();
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 5fa3d3e..6d781c7 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -2303,6 +2303,44 @@
     return DrawGlInfo::kStatusDrew;
 }
 
+void OpenGLRenderer::drawTextShadow(SkPaint* paint, const char* text, int bytesCount, int count,
+        const float* positions, FontRenderer& fontRenderer, int alpha, SkXfermode::Mode mode,
+        float x, float y) {
+    mCaches.activeTexture(0);
+
+    // NOTE: The drop shadow will not perform gamma correction
+    //       if shader-based correction is enabled
+    mCaches.dropShadowCache.setFontRenderer(fontRenderer);
+    const ShadowTexture* shadow = mCaches.dropShadowCache.get(
+            paint, text, bytesCount, count, mShadowRadius, positions);
+    const AutoTexture autoCleanup(shadow);
+
+    const float sx = x - shadow->left + mShadowDx;
+    const float sy = y - shadow->top + mShadowDy;
+
+    const int shadowAlpha = ((mShadowColor >> 24) & 0xFF) * mSnapshot->alpha;
+    int shadowColor = mShadowColor;
+    if (mShader) {
+        shadowColor = 0xffffffff;
+    }
+
+    setupDraw();
+    setupDrawWithTexture(true);
+    setupDrawAlpha8Color(shadowColor, shadowAlpha < 255 ? shadowAlpha : alpha);
+    setupDrawColorFilter();
+    setupDrawShader();
+    setupDrawBlending(true, mode);
+    setupDrawProgram();
+    setupDrawModelView(sx, sy, sx + shadow->width, sy + shadow->height);
+    setupDrawTexture(shadow->id);
+    setupDrawPureColorUniforms();
+    setupDrawColorFilterUniforms();
+    setupDrawShaderUniforms();
+    setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset);
+
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
+}
+
 status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count,
         const float* positions, SkPaint* paint) {
     if (text == NULL || count == 0 || mSnapshot->isIgnored() ||
@@ -2331,6 +2369,11 @@
     SkXfermode::Mode mode;
     getAlphaAndMode(paint, &alpha, &mode);
 
+    if (CC_UNLIKELY(mHasShadow)) {
+        drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer, alpha, mode,
+                0.0f, 0.0f);
+    }
+
     // Pick the appropriate texture filtering
     bool linearFilter = mSnapshot->transform->changesBounds();
     if (pureTranslate && !linearFilter) {
@@ -2424,39 +2467,7 @@
     getAlphaAndMode(paint, &alpha, &mode);
 
     if (CC_UNLIKELY(mHasShadow)) {
-        mCaches.activeTexture(0);
-
-        // NOTE: The drop shadow will not perform gamma correction
-        //       if shader-based correction is enabled
-        mCaches.dropShadowCache.setFontRenderer(fontRenderer);
-        const ShadowTexture* shadow = mCaches.dropShadowCache.get(
-                paint, text, bytesCount, count, mShadowRadius);
-        const AutoTexture autoCleanup(shadow);
-
-        const float sx = oldX - shadow->left + mShadowDx;
-        const float sy = oldY - shadow->top + mShadowDy;
-
-        const int shadowAlpha = ((mShadowColor >> 24) & 0xFF) * mSnapshot->alpha;
-        int shadowColor = mShadowColor;
-        if (mShader) {
-            shadowColor = 0xffffffff;
-        }
-
-        setupDraw();
-        setupDrawWithTexture(true);
-        setupDrawAlpha8Color(shadowColor, shadowAlpha < 255 ? shadowAlpha : alpha);
-        setupDrawColorFilter();
-        setupDrawShader();
-        setupDrawBlending(true, mode);
-        setupDrawProgram();
-        setupDrawModelView(sx, sy, sx + shadow->width, sy + shadow->height);
-        setupDrawTexture(shadow->id);
-        setupDrawPureColorUniforms();
-        setupDrawColorFilterUniforms();
-        setupDrawShaderUniforms();
-        setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset);
-
-        glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
+        drawTextShadow(paint, text, bytesCount, count, NULL, fontRenderer, alpha, mode, oldX, oldY);
     }
 
     // Pick the appropriate texture filtering
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 1926575..441e9fd 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -498,6 +498,24 @@
     void drawTextDecorations(const char* text, int bytesCount, float length,
             float x, float y, SkPaint* paint);
 
+   /**
+     * Draws shadow layer on text (with optional positions).
+     *
+     * @param paint The paint to draw the shadow with
+     * @param text The text to draw
+     * @param bytesCount The number of bytes in the text
+     * @param count The number of glyphs in the text
+     * @param positions The x, y positions of individual glyphs (or NULL)
+     * @param fontRenderer The font renderer object
+     * @param alpha The alpha value for drawing the shadow
+     * @param mode The xfermode for drawing the shadow
+     * @param x The x coordinate where the shadow will be drawn
+     * @param y The y coordinate where the shadow will be drawn
+     */
+    void drawTextShadow(SkPaint* paint, const char* text, int bytesCount, int count,
+            const float* positions, FontRenderer& fontRenderer, int alpha, SkXfermode::Mode mode,
+            float x, float y);
+
     /**
      * Draws a path texture. Path textures are alpha8 bitmaps that need special
      * compositing to apply colors/filters/etc.
@@ -507,7 +525,7 @@
      * @param y The y coordinate where the texture will be drawn
      * @param paint The paint to draw the texture with
      */
-    void drawPathTexture(const PathTexture* texture, float x, float y, SkPaint* paint);
+     void drawPathTexture(const PathTexture* texture, float x, float y, SkPaint* paint);
 
     /**
      * Resets the texture coordinates stored in mMeshVertices. Setting the values
diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp
index bef1373..93aa8a5 100644
--- a/libs/hwui/TextDropShadowCache.cpp
+++ b/libs/hwui/TextDropShadowCache.cpp
@@ -102,13 +102,13 @@
 }
 
 ShadowTexture* TextDropShadowCache::get(SkPaint* paint, const char* text, uint32_t len,
-        int numGlyphs, uint32_t radius) {
-    ShadowText entry(paint, radius, len, text);
+        int numGlyphs, uint32_t radius, const float* positions) {
+    ShadowText entry(paint, radius, len, text, positions);
     ShadowTexture* texture = mCache.get(entry);
 
     if (!texture) {
         FontRenderer::DropShadow shadow = mRenderer->renderDropShadow(paint, text, 0,
-                len, numGlyphs, radius);
+                len, numGlyphs, radius, positions);
 
         texture = new ShadowTexture;
         texture->left = shadow.penX;
diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h
index e2bdde1..bae0c49 100644
--- a/libs/hwui/TextDropShadowCache.h
+++ b/libs/hwui/TextDropShadowCache.h
@@ -35,8 +35,9 @@
     ShadowText(): radius(0), len(0), textSize(0.0f), typeface(NULL) {
     }
 
-    ShadowText(SkPaint* paint, uint32_t radius, uint32_t len, const char* srcText):
-            radius(radius), len(len) {
+    ShadowText(SkPaint* paint, uint32_t radius, uint32_t len, const char* srcText,
+            const float* positions):
+            radius(radius), len(len), positions(positions) {
         // TODO: Propagate this through the API, we should not cast here
         text = (const char16_t*) srcText;
 
@@ -66,11 +67,18 @@
     uint32_t italicStyle;
     uint32_t scaleX;
     const char16_t* text;
+    const float* positions;
     String16 str;
+    Vector<float> positionsCopy;
 
     void copyTextLocally() {
         str.setTo((const char16_t*) text, len >> 1);
         text = str.string();
+        if (positions != NULL) {
+            positionsCopy.clear();
+            positionsCopy.appendArray(positions, len);
+            positions = positionsCopy.array();
+        }
     }
 
     bool operator<(const ShadowText& rhs) const {
@@ -81,7 +89,12 @@
                         LTE_INT(flags) {
                             LTE_INT(italicStyle) {
                                 LTE_INT(scaleX) {
-                                    return memcmp(text, rhs.text, len) < 0;
+                                    int cmp = memcmp(text, rhs.text, len);
+                                    if (cmp < 0) return true;
+                                    if (cmp == 0 && rhs.positions != NULL) {
+                                        if (positions == NULL) return true;
+                                        return memcmp(positions, rhs.positions, len << 2) < 0;
+                                    }
                                 }
                             }
                         }
@@ -117,7 +130,7 @@
     void operator()(ShadowText& text, ShadowTexture*& texture);
 
     ShadowTexture* get(SkPaint* paint, const char* text, uint32_t len,
-            int numGlyphs, uint32_t radius);
+            int numGlyphs, uint32_t radius, const float* positions);
 
     /**
      * Clears the cache. This causes all textures to be deleted.
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Grain.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Grain.java
new file mode 100644
index 0000000..cd54c2e
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Grain.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.rs.image;
+
+import java.lang.Math;
+
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.RenderScript;
+import android.renderscript.Script;
+import android.renderscript.ScriptC;
+import android.renderscript.Type;
+import android.util.Log;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+public class Grain extends TestBase {
+    private ScriptC_grain mScript;
+    private Allocation mNoise;
+    private Allocation mNoise2;
+
+
+    public boolean onBar1Setup(SeekBar b, TextView t) {
+        t.setText("Strength");
+        b.setProgress(50);
+        return true;
+    }
+
+    public void onBar1Changed(int progress) {
+        float s = progress / 100.0f;
+        mScript.set_gNoiseStrength(s);
+    }
+
+    public void createTest(android.content.res.Resources res) {
+        int width = mInPixelsAllocation.getType().getX();
+        int height = mInPixelsAllocation.getType().getY();
+
+        Type.Builder tb = new Type.Builder(mRS, Element.U8(mRS));
+        tb.setX(width);
+        tb.setY(height);
+        mNoise = Allocation.createTyped(mRS, tb.create());
+        mNoise2 = Allocation.createTyped(mRS, tb.create());
+
+        mScript = new ScriptC_grain(mRS, res, R.raw.grain);
+        mScript.set_gWidth(width);
+        mScript.set_gHeight(height);
+        mScript.set_gNoiseStrength(0.5f);
+        mScript.set_gBlendSource(mNoise);
+        mScript.set_gNoise(mNoise2);
+    }
+
+    public void runTest() {
+        mScript.forEach_genRand(mNoise);
+        mScript.forEach_blend9(mNoise2);
+        mScript.forEach_root(mInPixelsAllocation, mOutPixelsAllocation);
+    }
+
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
index e085582..3a9838b 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
@@ -140,6 +140,9 @@
         case 5:
             mTest = new Greyscale();
             break;
+        case 6:
+            mTest = new Grain();
+            break;
         }
 
         mTest.createBaseTest(this, mBitmapIn);
@@ -152,13 +155,14 @@
     }
 
     void setupTests() {
-        mTestNames = new String[6];
+        mTestNames = new String[7];
         mTestNames[0] = "Levels Vec3 Relaxed";
         mTestNames[1] = "Levels Vec4 Relaxed";
         mTestNames[2] = "Levels Vec3 Full";
         mTestNames[3] = "Levels Vec4 Full";
         mTestNames[4] = "Blur radius 25";
         mTestNames[5] = "Greyscale";
+        mTestNames[6] = "Grain";
         mTestSpinner.setAdapter(new ArrayAdapter<String>(
             this, R.layout.spinner_layout, mTestNames));
     }
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/grain.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/grain.rs
new file mode 100644
index 0000000..97ae4fb
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/grain.rs
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.rs.image)
+#pragma rs_fp_relaxed
+
+void genRand(uchar *out) {
+    *out = (uchar)rsRand(0xff);
+}
+
+/*
+ * Convolution matrix of distance 2 with fixed point of 'kShiftBits' bits
+ * shifted. Thus the sum of this matrix should be 'kShiftValue'. Entries of
+ * small values are not calculated to gain efficiency.
+ * The order ot pixels represented in this matrix is:
+ *  1  2  3
+ *  4  0  5
+ *  6  7  8
+ *  and the matrix should be: {230, 56, 114, 56, 114, 114, 56, 114, 56}.
+ *  However, since most of the valus are identical, we only use the first three
+ *  entries and the entries corresponding to the pixels is:
+ *  1  2  1
+ *  2  0  2
+ *  1  2  1
+ */
+
+int32_t gWidth;
+int32_t gHeight;
+
+rs_allocation gBlendSource;
+void blend9(uchar *out, uint32_t x, uint32_t y) {
+    uint32_t x1 = min(x+1, (uint32_t)gWidth);
+    uint32_t x2 = max(x-1, (uint32_t)0);
+    uint32_t y1 = min(y+1, (uint32_t)gHeight);
+    uint32_t y2 = max(y-1, (uint32_t)0);
+
+    uint p00 = 56 *  ((uchar *)rsGetElementAt(gBlendSource, x1, y1))[0];
+    uint p01 = 114 * ((uchar *)rsGetElementAt(gBlendSource, x, y1))[0];
+    uint p02 = 56 *  ((uchar *)rsGetElementAt(gBlendSource, x2, y1))[0];
+    uint p10 = 114 * ((uchar *)rsGetElementAt(gBlendSource, x1, y))[0];
+    uint p11 = 230 * ((uchar *)rsGetElementAt(gBlendSource, x, y))[0];
+    uint p12 = 114 * ((uchar *)rsGetElementAt(gBlendSource, x2, y))[0];
+    uint p20 = 56 *  ((uchar *)rsGetElementAt(gBlendSource, x1, y2))[0];
+    uint p21 = 114 * ((uchar *)rsGetElementAt(gBlendSource, x, y2))[0];
+    uint p22 = 56 *  ((uchar *)rsGetElementAt(gBlendSource, x2, y2))[0];
+
+    p00 += p01;
+    p02 += p10;
+    p11 += p12;
+    p20 += p21;
+
+    p22 += p00;
+    p02 += p11;
+
+    p20 += p22;
+    p20 += p02;
+
+    *out = (uchar)(p20 >> 10);
+}
+
+float gNoiseStrength;
+
+rs_allocation gNoise;
+void root(const uchar4 *in, uchar4 *out, uint32_t x, uint32_t y) {
+    float4 ip = convert_float4(*in);
+    float pnoise = (float) ((uchar *)rsGetElementAt(gNoise, x, y))[0];
+
+    float energy_level = ip.r + ip.g + ip.b;
+    float energy_mask = (28.f - sqrt(energy_level)) * 0.03571f;
+    pnoise = (pnoise - 128.f) * energy_mask;
+
+    ip += pnoise * gNoiseStrength;
+    ip = clamp(ip, 0.f, 255.f);
+
+    uchar4 p = convert_uchar4(ip);
+    p.a = 0xff;
+    *out = p;
+}