Merge "Charge network traffic to the uid of the process using the MediaPlayer."
diff --git a/api/current.txt b/api/current.txt
index 5053859..aa08bb4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -596,10 +596,10 @@
     field public static final int layout_centerInParent = 16843151; // 0x101018f
     field public static final int layout_centerVertical = 16843153; // 0x1010191
     field public static final int layout_column = 16843084; // 0x101014c
-    field public static final int layout_columnSpan = 16843646; // 0x101037e
-    field public static final int layout_columnWeight = 16843647; // 0x101037f
+    field public static final int layout_columnSpan = 16843645; // 0x101037d
     field public static final int layout_gravity = 16842931; // 0x10100b3
     field public static final int layout_height = 16842997; // 0x10100f5
+    field public static final int layout_heightSpec = 16843647; // 0x101037f
     field public static final int layout_margin = 16842998; // 0x10100f6
     field public static final int layout_marginBottom = 16843002; // 0x10100fa
     field public static final int layout_marginEnd = 16843675; // 0x101039b
@@ -609,13 +609,13 @@
     field public static final int layout_marginTop = 16843000; // 0x10100f8
     field public static final int layout_row = 16843643; // 0x101037b
     field public static final int layout_rowSpan = 16843644; // 0x101037c
-    field public static final int layout_rowWeight = 16843645; // 0x101037d
     field public static final int layout_scale = 16843155; // 0x1010193
     field public static final int layout_span = 16843085; // 0x101014d
     field public static final int layout_toLeftOf = 16843138; // 0x1010182
     field public static final int layout_toRightOf = 16843139; // 0x1010183
     field public static final int layout_weight = 16843137; // 0x1010181
     field public static final int layout_width = 16842996; // 0x10100f4
+    field public static final int layout_widthSpec = 16843646; // 0x101037e
     field public static final int layout_x = 16843135; // 0x101017f
     field public static final int layout_y = 16843136; // 0x1010180
     field public static final int left = 16843181; // 0x10101ad
@@ -22075,6 +22075,7 @@
     method public boolean willNotDraw();
     field public static android.util.Property ALPHA;
     field protected static int DEFAULT_TEXT_DIRECTION;
+    field protected static float DEFAULT_TEXT_DIRECTION_CHAR_COUNT_THRESHOLD;
     field public static final int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
     field public static final int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000
     field public static final int DRAWING_CACHE_QUALITY_LOW = 524288; // 0x80000
@@ -24892,9 +24893,9 @@
   }
 
   public class GridLayout extends android.view.ViewGroup {
-    ctor public GridLayout(android.content.Context);
     ctor public GridLayout(android.content.Context, android.util.AttributeSet, int);
     ctor public GridLayout(android.content.Context, android.util.AttributeSet);
+    ctor public GridLayout(android.content.Context);
     method public int getAlignmentMode();
     method public int getColumnCount();
     method public int getOrientation();
@@ -24914,8 +24915,11 @@
     field public static final int ALIGN_MARGINS = 1; // 0x1
     field public static final android.widget.GridLayout.Alignment BASELINE;
     field public static final android.widget.GridLayout.Alignment BOTTOM;
+    field public static final android.widget.GridLayout.Spec CAN_SHRINK;
+    field public static final android.widget.GridLayout.Spec CAN_STRETCH;
     field public static final android.widget.GridLayout.Alignment CENTER;
     field public static final android.widget.GridLayout.Alignment FILL;
+    field public static final android.widget.GridLayout.Spec FIXED;
     field public static final int HORIZONTAL = 0; // 0x0
     field public static final android.widget.GridLayout.Alignment LEFT;
     field public static final android.widget.GridLayout.Alignment RIGHT;
@@ -24942,9 +24946,13 @@
     ctor public GridLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
     method public void setGravity(int);
     field public android.widget.GridLayout.Group columnGroup;
-    field public float columnWeight;
+    field public android.widget.GridLayout.Spec heightSpec;
     field public android.widget.GridLayout.Group rowGroup;
-    field public float rowWeight;
+    field public android.widget.GridLayout.Spec widthSpec;
+  }
+
+  public static abstract class GridLayout.Spec {
+    ctor public GridLayout.Spec();
   }
 
   public class GridView extends android.widget.AbsListView {
diff --git a/cmds/keystore/keystore.cpp b/cmds/keystore/keystore.cpp
index 1c1f37a..4b4b9b9 100644
--- a/cmds/keystore/keystore.cpp
+++ b/cmds/keystore/keystore.cpp
@@ -712,7 +712,6 @@
     {AID_VPN,      AID_SYSTEM, GET},
     {AID_WIFI,     AID_SYSTEM, GET},
     {AID_ROOT,     AID_SYSTEM, GET},
-    {AID_KEYCHAIN, AID_SYSTEM, TEST | GET | SAW},
     {~0,           ~0,         TEST | GET | INSERT | DELETE | EXIST | SAW},
 };
 
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index 41eea2e..42eda02 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -188,8 +188,9 @@
         mSearchPlate = mSearchView.findViewById(com.android.internal.R.id.search_plate);
         mWorkingSpinner = getContext().getResources().
                 getDrawable(com.android.internal.R.drawable.search_spinner);
-        mSearchAutoComplete.setCompoundDrawablesWithIntrinsicBounds(
-                null, null, mWorkingSpinner, null);
+        // TODO: Restore the spinner for slow suggestion lookups
+        // mSearchAutoComplete.setCompoundDrawablesWithIntrinsicBounds(
+        //        null, null, mWorkingSpinner, null);
         setWorking(false);
 
         // pre-hide all the extraneous elements
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index d475f36..05e39ac 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -92,12 +92,6 @@
     public static final int SDCARD_RW_GID = 1015;
 
     /**
-     * Defines the UID for the KeyChain service.
-     * @hide
-     */
-    public static final int KEYCHAIN_UID = 1020;
-
-    /**
      * Defines the UID/GID for the NFC service process.
      * @hide
      */
diff --git a/core/java/android/provider/VoicemailContract.java b/core/java/android/provider/VoicemailContract.java
index 41d3e71..d99c760 100644
--- a/core/java/android/provider/VoicemailContract.java
+++ b/core/java/android/provider/VoicemailContract.java
@@ -52,15 +52,18 @@
 
     /** The authority used by the voicemail provider. */
     public static final String AUTHORITY = "com.android.voicemail";
-    /** URI to insert/retrieve all voicemails. */
+    /**
+     * URI to insert/retrieve all voicemails.
+     * @deprecated
+     */
     public static final Uri CONTENT_URI =
             Uri.parse("content://" + AUTHORITY + "/voicemail");
-    /** URI to insert/retrieve voicemails by a given voicemail source. */
+    /**
+     * URI to insert/retrieve voicemails by a given voicemail source.
+     * @deprecated
+     */
     public static final Uri CONTENT_URI_SOURCE =
             Uri.parse("content://" + AUTHORITY + "/voicemail/source/");
-    /** URI to insert/retrieve status of voicemail source. */
-    public static final Uri STATUS_CONTENT_URI =
-            Uri.parse("content://" + AUTHORITY + "/status");
     /**
      * Parameter key used in the URI to specify the voicemail source package name.
      * <p> This field must be set in all requests that originate from a voicemail source.
@@ -77,33 +80,27 @@
      */
     public static final String EXTRA_SELF_CHANGE = "com.android.voicemail.extra.SELF_CHANGE";
 
-    /** The mime type for a collection of voicemails. */
+    /**
+     * The mime type for a collection of voicemails.
+     * @deprecated */
     public static final String DIR_TYPE = "vnd.android.cursor.dir/voicemails";
 
-    /**
-     * A convenience method to build voicemail URI specific to a source package. Appends URI param
-     * {@link #PARAM_KEY_SOURCE_PACKAGE} to the base voicemail content URI.
-     */
-    public static Uri buildSourceVoicemailUri(String packageName) {
-        return CONTENT_URI.buildUpon()
-                .appendQueryParameter(PARAM_KEY_SOURCE_PACKAGE, packageName).build();
-    }
-
-    /**
-     * A convenience method to build status URI specific to a source package. Appends URI param
-     * {@link #PARAM_KEY_SOURCE_PACKAGE} to the base status content URI.
-     */
-    public static Uri buildSourceStatusUri(String packageName) {
-        return STATUS_CONTENT_URI.buildUpon()
-                .appendQueryParameter(PARAM_KEY_SOURCE_PACKAGE, packageName).build();
-    }
-
     /** Defines fields exposed through the /voicemail path of this content provider. */
     public static final class Voicemails implements BaseColumns {
         /** Not instantiable. */
         private Voicemails() {
         }
 
+        /** URI to insert/retrieve voicemails by a given voicemail source. */
+        public static final Uri CONTENT_URI =
+            Uri.parse("content://" + AUTHORITY + "/voicemail");
+        /** URI to insert/retrieve voicemails by a given voicemail source. */
+        public static final Uri CONTENT_URI_SOURCE =
+                Uri.parse("content://" + AUTHORITY + "/voicemail/source/");
+
+        /** The mime type for a collection of voicemails. */
+        public static final String DIR_TYPE = "vnd.android.cursor.dir/voicemails";
+
         /**
          * Phone number of the voicemail sender.
          * <P>Type: TEXT</P>
@@ -167,10 +164,26 @@
          * @hide
          */
         public static final String _DATA = "_data";
+
+        /**
+         * A convenience method to build voicemail URI specific to a source package by appending
+         * {@link VoicemailContract#PARAM_KEY_SOURCE_PACKAGE} param to the base URI.
+         */
+        public static Uri buildSourceUri(String packageName) {
+            return Voicemails.CONTENT_URI.buildUpon()
+                    .appendQueryParameter(PARAM_KEY_SOURCE_PACKAGE, packageName).build();
+        }
     }
 
     /** Defines fields exposed through the /status path of this content provider. */
     public static final class Status implements BaseColumns {
+        /** URI to insert/retrieve status of voicemail source. */
+        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/status");
+        /** The mime type for a collection of voicemail source statuses. */
+        public static final String DIR_TYPE = "vnd.android.cursor.dir/voicemail.source.status";
+        /** The mime type for a collection of voicemails. */
+        public static final String ITEM_TYPE = "vnd.android.cursor.item/voicemail.source.status";
+
         /** Not instantiable. */
         private Status() {
         }
@@ -239,5 +252,13 @@
          */
         public static final int NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING = 2;
 
+        /**
+         * A convenience method to build status URI specific to a source package by appending
+         * {@link VoicemailContract#PARAM_KEY_SOURCE_PACKAGE} param to the base URI.
+         */
+        public static Uri buildSourceUri(String packageName) {
+            return Status.CONTENT_URI.buildUpon()
+                    .appendQueryParameter(PARAM_KEY_SOURCE_PACKAGE, packageName).build();
+        }
     }
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index bb5c954..1245898 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2523,18 +2523,26 @@
     public static final int TEXT_DIRECTION_ANY_RTL = 2;
 
     /**
+     * Text direction is the same as the one held by a 60% majority of the characters. If there is
+     * no majority then the paragraph direction is the resolved layout direction of the View.
+     *
+     * @hide
+     */
+    public static final int TEXT_DIRECTION_CHAR_COUNT = 3;
+
+    /**
      * Text direction is forced to LTR.
      *
      * @hide
      */
-    public static final int TEXT_DIRECTION_LTR = 3;
+    public static final int TEXT_DIRECTION_LTR = 4;
 
     /**
      * Text direction is forced to RTL.
      *
      * @hide
      */
-    public static final int TEXT_DIRECTION_RTL = 4;
+    public static final int TEXT_DIRECTION_RTL = 5;
 
     /**
      * Default text direction is inherited
@@ -2542,6 +2550,11 @@
     protected static int DEFAULT_TEXT_DIRECTION = TEXT_DIRECTION_INHERIT;
 
     /**
+     * Default threshold for "char count" heuristic.
+     */
+    protected static float DEFAULT_TEXT_DIRECTION_CHAR_COUNT_THRESHOLD = 0.6f;
+
+    /**
      * The text direction that has been defined by {@link #setTextDirection(int)}.
      *
      * {@hide}
@@ -2551,6 +2564,7 @@
             @ViewDebug.IntToString(from = TEXT_DIRECTION_INHERIT, to = "INHERIT"),
             @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG, to = "FIRST_STRONG"),
             @ViewDebug.IntToString(from = TEXT_DIRECTION_ANY_RTL, to = "ANY_RTL"),
+            @ViewDebug.IntToString(from = TEXT_DIRECTION_CHAR_COUNT, to = "CHAR_COUNT"),
             @ViewDebug.IntToString(from = TEXT_DIRECTION_LTR, to = "LTR"),
             @ViewDebug.IntToString(from = TEXT_DIRECTION_RTL, to = "RTL")
     })
@@ -11969,7 +11983,7 @@
         mPrivateFlags |= FORCE_LAYOUT;
         mPrivateFlags |= INVALIDATED;
 
-        if (mLayoutParams != null) {
+        if (mLayoutParams != null && mParent != null) {
             mLayoutParams.resolveWithDirection(getResolvedLayoutDirection());
         }
 
@@ -12996,6 +13010,7 @@
      * {@link #TEXT_DIRECTION_INHERIT},
      * {@link #TEXT_DIRECTION_FIRST_STRONG}
      * {@link #TEXT_DIRECTION_ANY_RTL},
+     * {@link #TEXT_DIRECTION_CHAR_COUNT},
      * {@link #TEXT_DIRECTION_LTR},
      * {@link #TEXT_DIRECTION_RTL},
      *
@@ -13013,6 +13028,7 @@
      * {@link #TEXT_DIRECTION_INHERIT},
      * {@link #TEXT_DIRECTION_FIRST_STRONG}
      * {@link #TEXT_DIRECTION_ANY_RTL},
+     * {@link #TEXT_DIRECTION_CHAR_COUNT},
      * {@link #TEXT_DIRECTION_LTR},
      * {@link #TEXT_DIRECTION_RTL},
      *
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index da88fbb..9f4ecbc 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -5036,6 +5036,7 @@
             // Pass down the hierarchy the following text direction values
             case TEXT_DIRECTION_FIRST_STRONG:
             case TEXT_DIRECTION_ANY_RTL:
+            case TEXT_DIRECTION_CHAR_COUNT:
             case TEXT_DIRECTION_LTR:
             case TEXT_DIRECTION_RTL:
                 resolvedTextDirection = mTextDirection;
diff --git a/core/java/android/webkit/HTML5VideoFullScreen.java b/core/java/android/webkit/HTML5VideoFullScreen.java
index 11ab0d7..0ea27a0 100644
--- a/core/java/android/webkit/HTML5VideoFullScreen.java
+++ b/core/java/android/webkit/HTML5VideoFullScreen.java
@@ -107,6 +107,7 @@
             // After we return from this we can't use the surface any more.
             // The current Video View will be destroy when we play a new video.
             pauseAndDispatch(mProxy);
+            mPlayer.release();
             mSurfaceHolder = null;
             if (mMediaController != null) {
                 mMediaController.hide();
@@ -226,6 +227,10 @@
                 mProxy.getWebView().getViewManager().showAll();
 
                 mProxy = null;
+
+                // Don't show the controller after exiting the full screen.
+                mMediaController = null;
+                mCurrentState = STATE_RELEASED;
             }
         };
 
diff --git a/core/java/android/webkit/HTML5VideoView.java b/core/java/android/webkit/HTML5VideoView.java
index 5983a44..67660b8 100644
--- a/core/java/android/webkit/HTML5VideoView.java
+++ b/core/java/android/webkit/HTML5VideoView.java
@@ -34,6 +34,7 @@
     static final int STATE_NOTPREPARED        = 1;
     static final int STATE_PREPARED           = 2;
     static final int STATE_PLAYING            = 3;
+    static final int STATE_RELEASED           = 4;
     protected int mCurrentState;
 
     protected HTML5VideoViewProxy mProxy;
@@ -84,7 +85,7 @@
     }
 
     public void pause() {
-        if (mCurrentState == STATE_PREPARED && mPlayer.isPlaying()) {
+        if (isPlaying()) {
             mPlayer.pause();
         } else if (mCurrentState == STATE_NOTPREPARED) {
             mPauseDuringPreparing = true;
@@ -120,11 +121,18 @@
     }
 
     public boolean isPlaying() {
-        return mPlayer.isPlaying();
+        if (mCurrentState == STATE_PREPARED) {
+            return mPlayer.isPlaying();
+        } else {
+            return false;
+        }
     }
 
     public void release() {
-        mPlayer.release();
+        if (mCurrentState != STATE_RELEASED) {
+            mPlayer.release();
+        }
+        mCurrentState = STATE_RELEASED;
     }
 
     public void stopPlayback() {
@@ -228,7 +236,7 @@
 
 
     public int getCurrentState() {
-        if (mPlayer.isPlaying()) {
+        if (isPlaying()) {
             return STATE_PLAYING;
         } else {
             return mCurrentState;
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index 1570224..7c0470e 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -24,6 +24,7 @@
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.Pair;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
@@ -33,7 +34,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -67,7 +67,7 @@
  *
  * <h4>Default Cell Assignment</h4>
  *
- * If no child specifies the row and column indices of the cell it
+ * If a child does not specify the row and column indices of the cell it
  * wishes to occupy, GridLayout assigns cell locations automatically using its:
  * {@link GridLayout#setOrientation(int) orientation},
  * {@link GridLayout#setRowCount(int) rowCount} and
@@ -94,8 +94,8 @@
  *
  * Like {@link LinearLayout}, a child's ability to stretch is controlled
  * using <em>weights</em>, which are specified using the
- * {@link GridLayout.LayoutParams#rowWeight rowWeight} and
- * {@link GridLayout.LayoutParams#columnWeight columnWeight} layout parameters.
+ * {@link GridLayout.LayoutParams#widthSpec widthSpec} and
+ * {@link GridLayout.LayoutParams#heightSpec heightSpec} layout parameters.
  * <p>
  * <p>
  * See {@link GridLayout.LayoutParams} for a full description of the
@@ -171,9 +171,7 @@
     private static final String TAG = GridLayout.class.getName();
     private static final boolean DEBUG = false;
     private static final double GOLDEN_RATIO = (1 + Math.sqrt(5)) / 2;
-    private static final int MIN = 0;
     private static final int PRF = 1;
-    private static final int MAX = 2;
 
     // Defaults
 
@@ -184,6 +182,7 @@
     private static final int DEFAULT_ALIGNMENT_MODE = ALIGN_MARGINS;
     // todo remove this
     private static final int DEFAULT_CONTAINER_MARGIN = 20;
+    private static final int MAX_SIZE = 100000;
 
     // TypedArray indices
 
@@ -205,36 +204,16 @@
     private int mAlignmentMode = DEFAULT_ALIGNMENT_MODE;
     private int mDefaultGravity = Gravity.NO_GRAVITY;
 
-    /* package */ boolean accommodateBothMinAndMax = false;
-
     // Constructors
 
     /**
      * {@inheritDoc}
      */
-    public GridLayout(Context context) {
-        this(context, null, 0);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
     public GridLayout(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
         if (DEBUG) {
             setWillNotDraw(false);
         }
-        processAttributes(context, attrs);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public GridLayout(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    private void processAttributes(Context context, AttributeSet attrs) {
         TypedArray a = context.obtainStyledAttributes(attrs, styleable.GridLayout);
         try {
             setRowCount(a.getInt(ROW_COUNT, DEFAULT_COUNT));
@@ -249,6 +228,20 @@
         }
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    public GridLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public GridLayout(Context context) {
+        this(context, null);
+    }
+
     // Implementation
 
     /**
@@ -527,11 +520,10 @@
         return result;
     }
 
-    private static int sum(float[] a) {
-        int result = 0;
-        for (int i = 0, length = a.length; i < length; i++) {
-            result += a[i];
-        }
+    private static <T> T[] append(T[] a, T[] b) {
+        T[] result = (T[]) Array.newInstance(a.getClass().getComponentType(), a.length + b.length);
+        System.arraycopy(a, 0, result, 0, a.length);
+        System.arraycopy(b, 0, result, a.length, b.length);
         return result;
     }
 
@@ -603,13 +595,13 @@
                     if (isGone(c)) continue;
                     LayoutParams lp = getLayoutParams1(c);
 
-                    Group colGroup = lp.columnGroup;
-                    Interval cols = colGroup.span;
-                    int colSpan = cols.size();
+                    final Group colGroup = lp.columnGroup;
+                    final Interval cols = colGroup.span;
+                    final int colSpan = cols.size();
 
-                    Group rowGroup = lp.rowGroup;
-                    Interval rows = rowGroup.span;
-                    int rowSpan = rows.size();
+                    final Group rowGroup = lp.rowGroup;
+                    final Interval rows = rowGroup.span;
+                    final int rowSpan = rows.size();
 
                     if (horizontal) {
                         row = valueIfDefined2(rows.min, row);
@@ -700,8 +692,8 @@
     }
 
     private void drawRectangle(Canvas graphics, int x1, int y1, int x2, int y2, Paint paint) {
-        // x2 = x2 - 1;
-        // y2 = y2 - 1;
+        x2 = x2 - 1;
+        y2 = y2 - 1;
         graphics.drawLine(x1, y1, x1, y2, paint);
         graphics.drawLine(x1, y1, x2, y1, paint);
         graphics.drawLine(x1, y2, x2, y2, paint);
@@ -734,9 +726,9 @@
                     drawLine(canvas, 0, y, width - 1, y, paint);
                 }
             }
+
             // Draw bounds
             paint.setColor(Color.BLUE);
-
             for (int i = 0; i < getChildCount(); i++) {
                 View c = getChildAt(i);
                 drawRectangle(canvas,
@@ -748,7 +740,6 @@
 
             // Draw margins
             paint.setColor(Color.YELLOW);
-
             for (int i = 0; i < getChildCount(); i++) {
                 View c = getChildAt(i);
                 drawRectangle(canvas,
@@ -819,11 +810,11 @@
     protected void onMeasure(int widthSpec, int heightSpec) {
         measureChildrenWithMargins(widthSpec, heightSpec);
 
-        int computedWidth = getPaddingLeft() + mHorizontalAxis.getMin() + getPaddingRight();
-        int computedHeight = getPaddingTop() + mVerticalAxis.getMin() + getPaddingBottom();
+        int width = getPaddingLeft() + mHorizontalAxis.getMeasure(widthSpec) + getPaddingRight();
+        int height = getPaddingTop() + mVerticalAxis.getMeasure(heightSpec) + getPaddingBottom();
 
-        int measuredWidth = Math.max(computedWidth, getSuggestedMinimumWidth());
-        int measuredHeight = Math.max(computedHeight, getSuggestedMinimumHeight());
+        int measuredWidth = Math.max(width, getSuggestedMinimumWidth());
+        int measuredHeight = Math.max(height, getSuggestedMinimumHeight());
 
         setMeasuredDimension(
                 resolveSizeAndState(measuredWidth, widthSpec, 0),
@@ -834,12 +825,12 @@
         return (alignment == UNDEFINED) ? 0 : alignment;
     }
 
-    private int getMeasurement(View c, boolean horizontal, int measurementType) {
+    private int getMeasurement(View c, boolean horizontal) {
         return horizontal ? c.getMeasuredWidth() : c.getMeasuredHeight();
     }
 
-    private int getMeasurementIncludingMargin(View c, boolean horizontal, int measurementType) {
-        int result = getMeasurement(c, horizontal, measurementType);
+    private int getMeasurementIncludingMargin(View c, boolean horizontal) {
+        int result = getMeasurement(c, horizontal);
         if (mAlignmentMode == ALIGN_MARGINS) {
             return result + getTotalMargin(c, horizontal);
         }
@@ -889,17 +880,17 @@
             Interval colSpan = columnGroup.span;
             Interval rowSpan = rowGroup.span;
 
-            int x1 = mHorizontalAxis.getLocationIncludingMargin(c, true, colSpan.min);
-            int y1 = mVerticalAxis.getLocationIncludingMargin(c, true, rowSpan.min);
+            int x1 = mHorizontalAxis.getLocationIncludingMargin(true, colSpan.min);
+            int y1 = mVerticalAxis.getLocationIncludingMargin(true, rowSpan.min);
 
-            int x2 = mHorizontalAxis.getLocationIncludingMargin(c, false, colSpan.max);
-            int y2 = mVerticalAxis.getLocationIncludingMargin(c, false, rowSpan.max);
+            int x2 = mHorizontalAxis.getLocationIncludingMargin(false, colSpan.max);
+            int y2 = mVerticalAxis.getLocationIncludingMargin(false, rowSpan.max);
 
             int cellWidth = x2 - x1;
             int cellHeight = y2 - y1;
 
-            int pWidth = getMeasurement(c, true, PRF);
-            int pHeight = getMeasurement(c, false, PRF);
+            int pWidth = getMeasurement(c, true);
+            int pHeight = getMeasurement(c, false);
 
             Alignment hAlign = columnGroup.alignment;
             Alignment vAlign = rowGroup.alignment;
@@ -910,9 +901,8 @@
             Bounds rowBounds = mVerticalAxis.getGroupBounds().getValue(i);
 
             // Gravity offsets: the location of the alignment group relative to its cell group.
-            int type = PRF;
-            int c2ax = protect(hAlign.getAlignmentValue(null, cellWidth - colBounds.size(), type));
-            int c2ay = protect(vAlign.getAlignmentValue(null, cellHeight - rowBounds.size(), type));
+            int c2ax = protect(hAlign.getAlignmentValue(null, cellWidth - colBounds.size(true)));
+            int c2ay = protect(vAlign.getAlignmentValue(null, cellHeight - rowBounds.size(true)));
 
             if (mAlignmentMode == ALIGN_MARGINS) {
                 int leftMargin = getMargin(c, true, true);
@@ -925,8 +915,8 @@
                 int mHeight = topMargin + pHeight + bottomMargin;
 
                 // Alignment offsets: the location of the view relative to its alignment group.
-                int a2vx = colBounds.getOffset(c, hAlign, type, mWidth);
-                int a2vy = rowBounds.getOffset(c, vAlign, type, mHeight);
+                int a2vx = colBounds.getOffset(c, hAlign, mWidth);
+                int a2vy = rowBounds.getOffset(c, vAlign, mHeight);
 
                 dx = c2ax + a2vx + leftMargin;
                 dy = c2ay + a2vy + topMargin;
@@ -935,13 +925,14 @@
                 cellHeight -= topMargin + bottomMargin;
             } else {
                 // Alignment offsets: the location of the view relative to its alignment group.
-                int a2vx = colBounds.getOffset(c, hAlign, type, pWidth);
-                int a2vy = rowBounds.getOffset(c, vAlign, type, pHeight);
+                int a2vx = colBounds.getOffset(c, hAlign, pWidth);
+                int a2vy = rowBounds.getOffset(c, vAlign, pHeight);
 
                 dx = c2ax + a2vx;
                 dy = c2ay + a2vy;
             }
 
+            int type = PRF;
             int width = hAlign.getSizeInCell(c, pWidth, cellWidth, type);
             int height = vAlign.getSizeInCell(c, pHeight, cellHeight, type);
 
@@ -962,7 +953,7 @@
     private class Axis {
         private static final int MIN_VALUE = -1000000;
 
-        private static final int UNVISITED = 0;
+        private static final int NEW = 0;
         private static final int PENDING = 1;
         private static final int COMPLETE = 2;
 
@@ -975,8 +966,11 @@
         PackedMap<Group, Bounds> groupBounds;
         public boolean groupBoundsValid = false;
 
-        PackedMap<Interval, MutableInt> spanSizes;
-        public boolean spanSizesValid = false;
+        PackedMap<Interval, MutableInt> forwardLinks;
+        public boolean forwardLinksValid = false;
+
+        PackedMap<Interval, MutableInt> backwardLinks;
+        public boolean backwardLinksValid = false;
 
         public int[] leadingMargins;
         public boolean leadingMarginsValid = false;
@@ -987,14 +981,14 @@
         public Arc[] arcs;
         public boolean arcsValid = false;
 
-        public int[] minima;
-        public boolean minimaValid = false;
-
-        public float[] weights;
         public int[] locations;
+        public boolean locationsValid = false;
 
         private boolean mOrderPreserved = DEFAULT_ORDER_PRESERVED;
 
+        private MutableInt parentMin = new MutableInt(0);
+        private MutableInt parentMax = new MutableInt(-MAX_SIZE);
+
         private Axis(boolean horizontal) {
             this.horizontal = horizontal;
         }
@@ -1036,22 +1030,19 @@
         }
 
         private PackedMap<Group, Bounds> createGroupBounds() {
-            int N = getChildCount();
-            Group[] groups = new Group[N];
-            Arrays.fill(groups, Group.GONE);
-            Bounds[] bounds = new Bounds[N];
-            Arrays.fill(bounds, Bounds.GONE);
-            for (int i = 0; i < N; i++) {
+            Assoc<Group, Bounds> assoc = Assoc.of(Group.class, Bounds.class);
+            for (int i = 0, N = getChildCount(); i < N; i++) {
                 View c = getChildAt(i);
-                if (isGone(c)) continue;
-                LayoutParams lp = getLayoutParams(c);
-                Group group = horizontal ? lp.columnGroup : lp.rowGroup;
-
-                groups[i] = group;
-                bounds[i] = group.alignment.getBounds();
+                if (isGone(c)) {
+                    assoc.put(Group.GONE, Bounds.GONE);
+                } else {
+                    LayoutParams lp = getLayoutParams(c);
+                    Group group = horizontal ? lp.columnGroup : lp.rowGroup;
+                    Bounds bounds = group.alignment.getBounds();
+                    assoc.put(group, bounds);
+                }
             }
-
-            return new PackedMap<Group, Bounds>(groups, bounds);
+            return assoc.pack();
         }
 
         private void computeGroupBounds() {
@@ -1064,13 +1055,7 @@
                 if (isGone(c)) continue;
                 LayoutParams lp = getLayoutParams(c);
                 Group g = horizontal ? lp.columnGroup : lp.rowGroup;
-
-                Bounds bounds = groupBounds.getValue(i);
-
-                int size = getMeasurementIncludingMargin(c, horizontal, PRF);
-                // todo test this works correctly when the returned value is UNDEFINED
-                int before = g.alignment.getAlignmentValue(c, size, PRF);
-                bounds.include(before, size - before);
+                groupBounds.getValue(i).include(c, g, GridLayout.this, this, lp);
             }
         }
 
@@ -1086,80 +1071,91 @@
         }
 
         // Add values computed by alignment - taking the max of all alignments in each span
-        private PackedMap<Interval, MutableInt> createSpanSizes() {
-            PackedMap<Group, Bounds> groupBounds = getGroupBounds();
-            int N = groupBounds.keys.length;
-            Interval[] spans = new Interval[N];
-            MutableInt[] values = new MutableInt[N];
-            for (int i = 0; i < N; i++) {
-                Interval key = groupBounds.keys[i].span;
-
-                spans[i] = key;
-                values[i] = new MutableInt();
+        private PackedMap<Interval, MutableInt> createLinks(boolean min) {
+            Assoc<Interval, MutableInt> result = Assoc.of(Interval.class, MutableInt.class);
+            Group[] keys = getGroupBounds().keys;
+            for (int i = 0, N = keys.length; i < N; i++) {
+                Interval span = min ? keys[i].span : keys[i].span.inverse();
+                result.put(span, new MutableInt());
             }
-            return new PackedMap<Interval, MutableInt>(spans, values);
+            return result.pack();
         }
 
-        private void computeSpanSizes() {
-            MutableInt[] spans = spanSizes.values;
+        private void computeLinks(PackedMap<Interval, MutableInt> links, boolean min) {
+            MutableInt[] spans = links.values;
             for (int i = 0; i < spans.length; i++) {
                 spans[i].reset();
             }
 
-            Bounds[] bounds = getGroupBounds().values;  // use getter to trigger a re-evaluation
+            // use getter to trigger a re-evaluation
+            Bounds[] bounds = getGroupBounds().values;
             for (int i = 0; i < bounds.length; i++) {
-                int value = bounds[i].size();
-
-                MutableInt valueHolder = spanSizes.getValue(i);
+                int size = bounds[i].size(min);
+                int value = min ? size : -size;
+                MutableInt valueHolder = links.getValue(i);
                 valueHolder.value = max(valueHolder.value, value);
             }
         }
 
-        private PackedMap<Interval, MutableInt> getSpanSizes() {
-            if (spanSizes == null) {
-                spanSizes = createSpanSizes();
+        private PackedMap<Interval, MutableInt> getForwardLinks() {
+            if (forwardLinks == null) {
+                forwardLinks = createLinks(true);
             }
-            if (!spanSizesValid) {
-                computeSpanSizes();
-                spanSizesValid = true;
+            if (!forwardLinksValid) {
+                computeLinks(forwardLinks, true);
+                forwardLinksValid = true;
             }
-            return spanSizes;
+            return forwardLinks;
         }
 
-        private void include(List<Arc> arcs, Interval key, MutableInt size) {
+        private PackedMap<Interval, MutableInt> getBackwardLinks() {
+            if (backwardLinks == null) {
+                backwardLinks = createLinks(false);
+            }
+            if (!backwardLinksValid) {
+                computeLinks(backwardLinks, false);
+                backwardLinksValid = true;
+            }
+            return backwardLinks;
+        }
+
+        private void include(List<Arc> arcs, Interval key, MutableInt size,
+                boolean ignoreIfAlreadyPresent) {
+            /*
+            Remove self referential links.
+            These appear:
+                . as parental constraints when GridLayout has no children
+                . when components have been marked as GONE
+            */
+            if (key.size() == 0) {
+                return;
+            }
             // this bit below should really be computed outside here -
-            // its just to stop default (col>0) constraints obliterating valid entries
-            for (Arc arc : arcs) {
-                Interval span = arc.span;
-                if (span.equals(key)) {
-                    return;
+            // its just to stop default (row/col > 0) constraints obliterating valid entries
+            if (ignoreIfAlreadyPresent) {
+                for (Arc arc : arcs) {
+                    Interval span = arc.span;
+                    if (span.equals(key)) {
+                        return;
+                    }
                 }
             }
             arcs.add(new Arc(key, size));
         }
 
-        private void include2(List<Arc> arcs, Interval span, MutableInt min, MutableInt max,
-                boolean both) {
-            include(arcs, span, min);
-            if (both) {
-                // todo
-//                include(arcs, span.inverse(), max.neg());
-            }
-        }
-
-        private void include2(List<Arc> arcs, Interval span, int min, int max, boolean both) {
-            include2(arcs, span, new MutableInt(min), new MutableInt(max), both);
+        private void include(List<Arc> arcs, Interval key, MutableInt size) {
+            include(arcs, key, size, true);
         }
 
         // Group arcs by their first vertex, returning an array of arrays.
         // This is linear in the number of arcs.
         private Arc[][] groupArcsByFirstVertex(Arc[] arcs) {
-            int N = getCount() + 1;// the number of vertices
+            int N = getCount() + 1; // the number of vertices
             Arc[][] result = new Arc[N][];
             int[] sizes = new int[N];
             for (Arc arc : arcs) {
                 sizes[arc.span.min]++;
-            }
+           }
             for (int i = 0; i < sizes.length; i++) {
                 result[i] = new Arc[sizes[i]];
             }
@@ -1173,38 +1169,46 @@
             return result;
         }
 
-        private Arc[] topologicalSort(final Arc[] arcs, int start) {
-            // todo ensure the <start> vertex is added in edge cases
-            final List<Arc> result = new ArrayList<Arc>();
-            new Object() {
-                Arc[][] arcsByFirstVertex = groupArcsByFirstVertex(arcs);
+        private Arc[] topologicalSort(final Arc[] arcs) {
+            return new Object() {
+                Arc[] result = new Arc[arcs.length];
+                int cursor = result.length - 1;
+                Arc[][] arcsByVertex = groupArcsByFirstVertex(arcs);
                 int[] visited = new int[getCount() + 1];
 
-                boolean completesCycle(int loc) {
-                    int state = visited[loc];
-                    if (state == UNVISITED) {
-                        visited[loc] = PENDING;
-                        for (Arc arc : arcsByFirstVertex[loc]) {
-                            Interval span = arc.span;
-                            // the recursive call
-                            if (completesCycle(span.max)) {
-                                // which arcs get set here is dependent on the order
-                                // in which we explore nodes
-                                arc.completesCycle = true;
+                void walk(int loc) {
+                    switch (visited[loc]) {
+                        case NEW: {
+                            visited[loc] = PENDING;
+                            for (Arc arc : arcsByVertex[loc]) {
+                                walk(arc.span.max);
+                                result[cursor--] = arc;
                             }
-                            result.add(arc);
+                            visited[loc] = COMPLETE;
+                            break;
                         }
-                        visited[loc] = COMPLETE;
-                    } else if (state == PENDING) {
-                        return true;
-                    } else if (state == COMPLETE) {
+                        case PENDING: {
+                            assert false;
+                            break;
+                        }
+                        case COMPLETE: {
+                            break;
+                        }
                     }
-                    return false;
                 }
-            }.completesCycle(start);
-            Collections.reverse(result);
-            assert arcs.length == result.size();
-            return result.toArray(new Arc[result.size()]);
+
+                Arc[] sort() {
+                    for (int loc = 0, N = arcsByVertex.length; loc < N; loc++) {
+                        walk(loc);
+                    }
+                    assert cursor == -1;
+                    return result;
+                }
+            }.sort();
+        }
+
+        private Arc[] topologicalSort(List<Arc> arcs) {
+            return topologicalSort(arcs.toArray(new Arc[arcs.size()]));
         }
 
         private boolean[] findUsed(Collection<Arc> arcs) {
@@ -1254,43 +1258,64 @@
             return result;
         }
 
-        private Arc[] createArcs() {
-            List<Arc> result = new ArrayList<Arc>();
-
-            // Add all the preferred elements that were not defined by the user.
-            PackedMap<Interval, MutableInt> spanSizes = getSpanSizes();
-            for (int i = 0; i < spanSizes.keys.length; i++) {
-                Interval key = spanSizes.keys[i];
-                if (key == Interval.GONE) continue;
-                MutableInt value = spanSizes.values[i];
-                // todo remove value duplicate
-                include2(result, key, value, value, accommodateBothMinAndMax);
+        private void addComponentSizes(List<Arc> result, PackedMap<Interval, MutableInt> links) {
+            for (int i = 0; i < links.keys.length; i++) {
+                Interval key = links.keys[i];
+                include(result, key, links.values[i], false);
             }
+        }
+
+        private Arc[] createArcs() {
+            List<Arc> mins = new ArrayList<Arc>();
+            List<Arc> maxs = new ArrayList<Arc>();
+
+            // Add the minimum values from the components.
+            addComponentSizes(mins, getForwardLinks());
+            // Add the maximum values from the components.
+            addComponentSizes(maxs, getBackwardLinks());
 
             // Find redundant rows/cols and glue them together with 0-length arcs to link the tree
-            boolean[] used = findUsed(result);
+            boolean[] used = findUsed(mins);
             for (int i = 0; i < getCount(); i++) {
                 if (!used[i]) {
                     Interval span = new Interval(i, i + 1);
-                    include(result, span, new MutableInt(0));
-                    include(result, span.inverse(), new MutableInt(0));
+                    include(mins, span, new MutableInt(0));
+                    include(maxs, span.inverse(), new MutableInt(0));
                 }
             }
 
+            // Add ordering constraints to prevent row/col sizes from going negative
             if (mOrderPreserved) {
-                // Add preferred gaps
+                // Add a constraint for every row/col
                 for (int i = 0; i < getCount(); i++) {
                     if (used[i]) {
-                        include2(result, new Interval(i, i + 1), 0, 0, false);
+                        include(mins, new Interval(i, i + 1), new MutableInt(0));
                     }
                 }
             } else {
+                // Add a constraint for each row/col that separates opposing component edges
                 for (Interval gap : getSpacers()) {
-                    include2(result, gap, 0, 0, false);
+                    include(mins, gap, new MutableInt(0));
                 }
             }
-            Arc[] arcs = result.toArray(new Arc[result.size()]);
-            return topologicalSort(arcs, 0);
+
+            // Add the container constraints. Use the version of include that allows
+            // duplicate entries in case a child spans the entire grid.
+            int N = getCount();
+            include(mins, new Interval(0, N), parentMin, false);
+            include(maxs, new Interval(N, 0), parentMax, false);
+
+            // Sort
+            Arc[] sMins = topologicalSort(mins);
+            Arc[] sMaxs = topologicalSort(maxs);
+
+            return append(sMins, sMaxs);
+        }
+
+        private void computeArcs() {
+            // getting the links validates the values that are shared by the arc list
+            getForwardLinks();
+            getBackwardLinks();
         }
 
         public Arc[] getArcs() {
@@ -1298,13 +1323,16 @@
                 arcs = createArcs();
             }
             if (!arcsValid) {
-                getSpanSizes();
+                computeArcs();
                 arcsValid = true;
             }
             return arcs;
         }
 
         private boolean relax(int[] locations, Arc entry) {
+            if (!entry.valid) {
+                return false;
+            }
             Interval span = entry.span;
             int u = span.min;
             int v = span.max;
@@ -1351,7 +1379,8 @@
         typical layout problems complete after the first iteration and the algorithm
         completes in O(N) steps with very low constants.
         */
-        private int[] solve(Arc[] arcs, int[] locations) {
+        private void solve(Arc[] arcs, int[] locations) {
+            String axis = horizontal ? "horizontal" : "vertical";
             int N = getCount() + 1; // The number of vertices is the number of columns/rows + 1.
 
             boolean changed = false;
@@ -1359,20 +1388,44 @@
             for (int i = 0; i < N; i++) {
                 changed = false;
                 for (int j = 0, length = arcs.length; j < length; j++) {
-                    changed = changed | relax(locations, arcs[j]);
+                    changed |= relax(locations, arcs[j]);
                 }
                 if (!changed) {
                     if (DEBUG) {
-                        Log.d(TAG, "Iteration " +
-                                " completed after " + (1 + i) + " steps out of " + N);
+                        Log.d(TAG, axis + " iteration completed in " + (1 + i) + " steps of " + N);
                     }
+                    return;
+                }
+            }
+
+            Log.d(TAG, "The " + axis + " constraints contained a contradiction. Resolving... ");
+            Log.d(TAG, Arrays.toString(arcs));
+
+            boolean[] culprits = new boolean[arcs.length];
+            for (int i = 0; i < N; i++) {
+                for (int j = 0, length = arcs.length; j < length; j++) {
+                    culprits[j] |= relax(locations, arcs[j]);
+                }
+            }
+            for (int i = 0; i < culprits.length; i++) {
+                if (culprits[i]) {
+                    Arc arc = arcs[i];
+                    // Only remove max values, min values alone cannot be inconsistent
+                    if (arc.span.min < arc.span.max) {
+                        continue;
+                    }
+                    Log.d(TAG, "Removing: " + arc);
+                    arc.valid = false;
                     break;
                 }
             }
-            if (changed) {
-                Log.d(TAG, "*** Algorithm failed to terminate ***");
-            }
-            return locations;
+            solve1(arcs, locations);
+        }
+
+        private void solve1(Arc[] arcs, int[] a) {
+            Arrays.fill(a, MIN_VALUE);
+            a[0] = 0;
+            solve(arcs, a);
         }
 
         private void computeMargins(boolean leading) {
@@ -1418,11 +1471,11 @@
             for (int i = 0, N = getCount(); i < N; i++) {
                 int margins = leadingMargins[i] + trailingMargins[i + 1];
                 delta += margins;
-                minima[i + 1] += delta;
+                locations[i + 1] += delta;
             }
         }
 
-        private int getLocationIncludingMargin(View view, boolean leading, int index) {
+        private int getLocationIncludingMargin(boolean leading, int index) {
             int location = locations[index];
             int margin;
             if (mAlignmentMode != ALIGN_MARGINS) {
@@ -1433,53 +1486,22 @@
             return leading ? (location + margin) : (location - margin);
         }
 
-        private void computeMinima(int[] a) {
-            Arrays.fill(a, MIN_VALUE);
-            a[0] = 0;
-            solve(getArcs(), a);
+        private void computeLocations(int[] a) {
+            solve1(getArcs(), a);
             if (mAlignmentMode != ALIGN_MARGINS) {
                 addMargins();
             }
         }
 
-        private int[] getMinima() {
-            if (minima == null) {
-                int N = getCount() + 1;
-                minima = new int[N];
-            }
-            if (!minimaValid) {
-                computeMinima(minima);
-                minimaValid = true;
-            }
-            return minima;
-        }
-
-        private void computeWeights() {
-            for (int i = 0, N = getChildCount(); i < N; i++) {
-                View c = getChildAt(i);
-                if (isGone(c)) continue;
-                LayoutParams lp = getLayoutParams(c);
-                Group g = horizontal ? lp.columnGroup : lp.rowGroup;
-                Interval span = g.span;
-                int penultimateIndex = span.max - 1;
-                weights[penultimateIndex] += horizontal ? lp.columnWeight : lp.rowWeight;
-            }
-        }
-
-        private float[] getWeights() {
-            if (weights == null) {
-                int N = getCount();
-                weights = new float[N];
-            }
-            computeWeights();
-            return weights;
-        }
-
         private int[] getLocations() {
             if (locations == null) {
                 int N = getCount() + 1;
                 locations = new int[N];
             }
+            if (!locationsValid) {
+                computeLocations(locations);
+                locationsValid = true;
+            }
             return locations;
         }
 
@@ -1489,48 +1511,53 @@
             return max2(locations, 0) - locations[0];
         }
 
-        private int getMin() {
-            return size(getMinima());
+        private void setParentConstraints(int min, int max) {
+            parentMin.value = min;
+            parentMax.value = -max;
+            locationsValid = false;
         }
 
-        private void layout(int targetSize) {
-            int[] mins = getMinima();
+        private int getMeasure(int min, int max) {
+            setParentConstraints(min, max);
+            return size(getLocations());
+        }
 
-            int totalDelta = max(0, targetSize - size(mins)); // confine to expansion
-
-            float[] weights = getWeights();
-            float totalWeight = sum(weights);
-
-            if (totalWeight == 0f && weights.length > 0) {
-                weights[weights.length - 1] = 1;
-                totalWeight = 1;
+        private int getMeasure(int measureSpec) {
+            int mode = MeasureSpec.getMode(measureSpec);
+            int size = MeasureSpec.getSize(measureSpec);
+            switch (mode) {
+                case MeasureSpec.UNSPECIFIED: {
+                     return getMeasure(0, MAX_SIZE);
+                }
+                case MeasureSpec.EXACTLY: {
+                    return getMeasure(size, size);
+                }
+                case MeasureSpec.AT_MOST: {
+                    return getMeasure(0, size);
+                }
+                default: {
+                    assert false;
+                    return 0;
+                }
             }
+        }
 
-            int[] locations = getLocations();
-            int cumulativeDelta = 0;
-
-            // note |weights| = |locations| - 1
-            for (int i = 0; i < weights.length; i++) {
-                float weight = weights[i];
-                int delta = (int) (totalDelta * weight / totalWeight);
-                cumulativeDelta += delta;
-                locations[i + 1] = mins[i + 1] + cumulativeDelta;
-
-                totalDelta -= delta;
-                totalWeight -= weight;
-            }
+        private void layout(int size) {
+            setParentConstraints(size, size);
+            getLocations();
         }
 
         private void invalidateStructure() {
             countValid = false;
 
             groupBounds = null;
-            spanSizes = null;
+            forwardLinks = null;
+            backwardLinks = null;
+
             leadingMargins = null;
             trailingMargins = null;
             arcs = null;
-            minima = null;
-            weights = null;
+
             locations = null;
 
             invalidateValues();
@@ -1538,11 +1565,14 @@
 
         private void invalidateValues() {
             groupBoundsValid = false;
-            spanSizesValid = false;
-            arcsValid = false;
+            forwardLinksValid = false;
+            backwardLinksValid = false;
+
             leadingMarginsValid = false;
             trailingMarginsValid = false;
-            minimaValid = false;
+            arcsValid = false;
+
+            locationsValid = false;
         }
     }
 
@@ -1592,16 +1622,16 @@
      *     <li>{@link #rowGroup}{@code .alignment} = {@link #BASELINE} </li>
      *     <li>{@link #columnGroup}{@code .span} = {@code [0, 1]} </li>
      *     <li>{@link #columnGroup}{@code .alignment} = {@link #LEFT} </li>
-     *     <li>{@link #rowWeight} = {@code 0f} </li>
-     *     <li>{@link #columnWeight} = {@code 0f} </li>
+     *     <li>{@link #widthSpec} = {@link #FIXED} </li>
+     *     <li>{@link #heightSpec} = {@link #FIXED} </li>
      * </ul>
      *
      * @attr ref android.R.styleable#GridLayout_Layout_layout_row
      * @attr ref android.R.styleable#GridLayout_Layout_layout_rowSpan
-     * @attr ref android.R.styleable#GridLayout_Layout_layout_rowWeight
+     * @attr ref android.R.styleable#GridLayout_Layout_layout_heightSpec
      * @attr ref android.R.styleable#GridLayout_Layout_layout_column
      * @attr ref android.R.styleable#GridLayout_Layout_layout_columnSpan
-     * @attr ref android.R.styleable#GridLayout_Layout_layout_columnWeight
+     * @attr ref android.R.styleable#GridLayout_Layout_layout_widthSpec
      * @attr ref android.R.styleable#GridLayout_Layout_layout_gravity
      */
     public static class LayoutParams extends MarginLayoutParams {
@@ -1621,14 +1651,15 @@
                 new Group(DEFAULT_SPAN, DEFAULT_COLUMN_ALIGNMENT);
         private static final Group DEFAULT_ROW_GROUP =
                 new Group(DEFAULT_SPAN, DEFAULT_ROW_ALIGNMENT);
-        private static final int DEFAULT_WEIGHT_0 = 0;
-        private static final int DEFAULT_WEIGHT_1 = 1;
+        private static final Spec DEFAULT_SPEC = FIXED;
+        private static final int DEFAULT_SPEC_INDEX = 0;
 
         // Misc
 
         private static final Rect CONTAINER_BOUNDS = new Rect(0, 0, 2, 2);
         private static final Alignment[] COLUMN_ALIGNMENTS = { LEFT, CENTER, RIGHT };
         private static final Alignment[] ROW_ALIGNMENTS = { TOP, CENTER, BOTTOM };
+        private static final Spec[] SPECS = { FIXED, CAN_SHRINK, CAN_STRETCH };
 
         // TypedArray indices
 
@@ -1641,10 +1672,10 @@
 
         private static final int COLUMN = styleable.GridLayout_Layout_layout_column;
         private static final int COLUMN_SPAN = styleable.GridLayout_Layout_layout_columnSpan;
-        private static final int COLUMN_WEIGHT = styleable.GridLayout_Layout_layout_columnWeight;
+        private static final int WIDTH_SPEC = styleable.GridLayout_Layout_layout_widthSpec;
         private static final int ROW = styleable.GridLayout_Layout_layout_row;
         private static final int ROW_SPAN = styleable.GridLayout_Layout_layout_rowSpan;
-        private static final int ROW_WEIGHT = styleable.GridLayout_Layout_layout_rowWeight;
+        private static final int HEIGHT_SPEC = styleable.GridLayout_Layout_layout_heightSpec;
         private static final int GRAVITY = styleable.GridLayout_Layout_layout_gravity;
 
         // Instance variables
@@ -1660,28 +1691,29 @@
          */
         public Group columnGroup;
         /**
-         * The proportional space that should be taken by the associated row group
-         * during excess space distribution.
-         */
-        public float rowWeight;
-        /**
          * The proportional space that should be taken by the associated column group
          * during excess space distribution.
          */
-        public float columnWeight;
+        public Spec widthSpec;
+        /**
+         * The proportional space that should be taken by the associated row group
+         * during excess space distribution.
+         */
+        public Spec heightSpec;
 
         // Constructors
 
         private LayoutParams(
                 int width, int height,
                 int left, int top, int right, int bottom,
-                Group rowGroup, Group columnGroup, float rowWeight, float columnWeight) {
+                Group rowGroup, Group columnGroup,
+                Spec widthSpec, Spec heightSpec) {
             super(width, height);
             setMargins(left, top, right, bottom);
             this.rowGroup = rowGroup;
             this.columnGroup = columnGroup;
-            this.rowWeight = rowWeight;
-            this.columnWeight = columnWeight;
+            this.heightSpec = heightSpec;
+            this.widthSpec = widthSpec;
         }
 
         /**
@@ -1695,7 +1727,7 @@
         public LayoutParams(Group rowGroup, Group columnGroup) {
             this(DEFAULT_WIDTH, DEFAULT_HEIGHT,
                     DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN,
-                    rowGroup, columnGroup, DEFAULT_WEIGHT_0, DEFAULT_WEIGHT_0);
+                    rowGroup, columnGroup, DEFAULT_SPEC, DEFAULT_SPEC);
         }
 
         /**
@@ -1728,8 +1760,8 @@
             super(that);
             this.columnGroup = that.columnGroup;
             this.rowGroup = that.rowGroup;
-            this.columnWeight = that.columnWeight;
-            this.rowWeight = that.rowWeight;
+            this.widthSpec = that.widthSpec;
+            this.heightSpec = that.heightSpec;
         }
 
         // AttributeSet constructors
@@ -1813,11 +1845,6 @@
                     !definesVertical(gravity), defaultAlignment);
         }
 
-        private int getDefaultWeight(int size) {
-            //return (size == MATCH_PARENT) ? DEFAULT_WEIGHT_1 : DEFAULT_WEIGHT_0;
-            return DEFAULT_WEIGHT_0;
-        }
-
         private void init(Context context, AttributeSet attrs, int defaultGravity) {
             TypedArray a = context.obtainStyledAttributes(attrs, styleable.GridLayout_Layout);
             try {
@@ -1827,13 +1854,13 @@
                 int columnSpan = a.getInt(COLUMN_SPAN, DEFAULT_SPAN_SIZE);
                 Interval hSpan = new Interval(column, column + columnSpan);
                 this.columnGroup = new Group(hSpan, getColumnAlignment(gravity, width));
-                this.columnWeight = a.getFloat(COLUMN_WEIGHT, getDefaultWeight(width));
+                this.widthSpec = SPECS[a.getInt(WIDTH_SPEC, DEFAULT_SPEC_INDEX)];
 
                 int row = a.getInt(ROW, DEFAULT_ROW);
                 int rowSpan = a.getInt(ROW_SPAN, DEFAULT_SPAN_SIZE);
                 Interval vSpan = new Interval(row, row + rowSpan);
                 this.rowGroup = new Group(vSpan, getRowAlignment(gravity, height));
-                this.rowWeight = a.getFloat(ROW_WEIGHT, getDefaultWeight(height));
+                this.heightSpec = SPECS[a.getInt(HEIGHT_SPEC, DEFAULT_SPEC_INDEX)];
             } finally {
                 a.recycle();
             }
@@ -1874,7 +1901,7 @@
     private static class Arc {
         public final Interval span;
         public final MutableInt value;
-        public boolean completesCycle;
+        public boolean valid = true;
 
         public Arc(Interval span, MutableInt value) {
             this.span = span;
@@ -1883,7 +1910,7 @@
 
         @Override
         public String toString() {
-            return span + " " + (completesCycle ? "+>" : "->") + " " + value;
+            return span + " " + (!valid ? "+>" : "->") + " " + value;
         }
     }
 
@@ -1903,6 +1930,41 @@
         private void reset() {
             value = Integer.MIN_VALUE;
         }
+
+        @Override
+        public String toString() {
+            return Integer.toString(value);
+        }
+    }
+
+    private static class Assoc<K, V> extends ArrayList<Pair<K, V>> {
+        private final Class<K> keyType;
+        private final Class<V> valueType;
+
+        private Assoc(Class<K> keyType, Class<V> valueType) {
+            this.keyType = keyType;
+            this.valueType = valueType;
+        }
+
+        private static <K, V> Assoc<K, V> of(Class<K> keyType, Class<V> valueType) {
+            return new Assoc<K, V>(keyType, valueType);
+        }
+
+        public void put(K key, V value) {
+            add(Pair.create(key, value));
+        }
+
+        @SuppressWarnings(value = "unchecked")
+        public PackedMap<K, V> pack() {
+            int N = size();
+            K[] keys = (K[]) Array.newInstance(keyType, N);
+            V[] values = (V[]) Array.newInstance(valueType, N);
+            for (int i = 0; i < N; i++) {
+                keys[i] = get(i).first;
+                values[i] = get(i).second;
+            }
+            return new PackedMap<K, V>(keys, values);
+        }
     }
 
     /*
@@ -1989,6 +2051,7 @@
 
         public int before;
         public int after;
+        public boolean canStretch;
 
         private Bounds() {
             reset();
@@ -1997,6 +2060,7 @@
         protected void reset() {
             before = Integer.MIN_VALUE;
             after = Integer.MIN_VALUE;
+            canStretch = false;
         }
 
         protected void include(int before, int after) {
@@ -2004,12 +2068,26 @@
             this.after = max(this.after, after);
         }
 
-        protected int size() {
+        protected int size(boolean min) {
+            if (!min && canStretch) {
+                return MAX_SIZE;
+            }
             return before + after;
         }
 
-        protected int getOffset(View c, Alignment alignment, int type, int size) {
-            return before - alignment.getAlignmentValue(c, size, type);
+        protected int getOffset(View c, Alignment alignment, int size) {
+            return before - alignment.getAlignmentValue(c, size);
+        }
+
+        protected void include(View c, Group g, GridLayout gridLayout, Axis axis, LayoutParams lp) {
+            Spec spec = axis.horizontal ? lp.widthSpec : lp.heightSpec;
+            if (spec == CAN_STRETCH) {
+                canStretch = true;
+            }
+            int size = gridLayout.getMeasurementIncludingMargin(c, axis.horizontal);
+            // todo test this works correctly when the returned value is UNDEFINED
+            int before = g.alignment.getAlignmentValue(c, size);
+            include(before, size - before);
         }
 
         @Override
@@ -2032,7 +2110,7 @@
      * Intervals are often written as {@code [min, max]} and represent the set of values
      * {@code x} such that {@code min <= x < max}.
      */
-    /* package */ static class Interval {
+    static class Interval {
         private static final Interval GONE = new Interval(UNDEFINED, UNDEFINED);
 
         /**
@@ -2129,7 +2207,7 @@
          * See {@link GridLayout} for a description of the conventions used by GridLayout
          * for grid indices.
          */
-        /* package */ final Interval span;
+        final Interval span;
         /**
          * Specifies how cells should be aligned in this group.
          * For row groups, this specifies the vertical alignment.
@@ -2147,7 +2225,7 @@
          * @param span      the span
          * @param alignment the alignment
          */
-        /* package */ Group(Interval span, Alignment alignment) {
+        Group(Interval span, Alignment alignment) {
             this.span = span;
             this.alignment = alignment;
         }
@@ -2248,17 +2326,16 @@
      * so that the locations defined by the alignment values
      * are the same for all of the views in a group.
      * <p>
-
      */
     public static abstract class Alignment {
         private static final Alignment GONE = new Alignment() {
-            public int getAlignmentValue(View view, int viewSize, int measurementType) {
+            public int getAlignmentValue(View view, int viewSize) {
                 assert false;
                 return 0;
             }
         };
 
-        /*pp*/ Alignment() {
+        Alignment() {
         }
 
         /**
@@ -2269,12 +2346,9 @@
          *
          * @param view              the view to which this alignment should be applied
          * @param viewSize          the measured size of the view
-         * @param measurementType   This parameter is currently unused as GridLayout only supports
-         *                          one type of measurement: {@link View#measure(int, int)}.
-         *
          * @return the alignment value
          */
-        /*pp*/ abstract int getAlignmentValue(View view, int viewSize, int measurementType);
+        abstract int getAlignmentValue(View view, int viewSize);
 
         /**
          * Returns the size of the view specified by this alignment.
@@ -2291,24 +2365,24 @@
          *
          * @return the aligned size
          */
-        /*pp*/ int getSizeInCell(View view, int viewSize, int cellSize, int measurementType) {
+        int getSizeInCell(View view, int viewSize, int cellSize, int measurementType) {
             return viewSize;
         }
 
-        /*pp*/ Bounds getBounds() {
+        Bounds getBounds() {
             return new Bounds();
         }
     }
 
     private static final Alignment LEADING = new Alignment() {
-        public int getAlignmentValue(View view, int viewSize, int measurementType) {
+        public int getAlignmentValue(View view, int viewSize) {
             return 0;
         }
 
     };
 
     private static final Alignment TRAILING = new Alignment() {
-        public int getAlignmentValue(View view, int viewSize, int measurementType) {
+        public int getAlignmentValue(View view, int viewSize) {
             return viewSize;
         }
     };
@@ -2343,7 +2417,7 @@
      * LayoutParams#columnGroup columnGroups}.
      */
     public static final Alignment CENTER = new Alignment() {
-        public int getAlignmentValue(View view, int viewSize, int measurementType) {
+        public int getAlignmentValue(View view, int viewSize) {
             return viewSize >> 1;
         }
     };
@@ -2356,7 +2430,7 @@
      * @see View#getBaseline()
      */
     public static final Alignment BASELINE = new Alignment() {
-        public int getAlignmentValue(View view, int viewSize, int measurementType) {
+        public int getAlignmentValue(View view, int viewSize) {
             if (view == null) {
                 return UNDEFINED;
             }
@@ -2378,7 +2452,7 @@
                 @Override
                 protected void reset() {
                     super.reset();
-                    size = 0;
+                    size = Integer.MIN_VALUE;
                 }
 
                 @Override
@@ -2388,13 +2462,13 @@
                 }
 
                 @Override
-                protected int size() {
-                    return max(super.size(), size);
+                protected int size(boolean min) {
+                    return max(super.size(min), size);
                 }
 
                 @Override
-                protected int getOffset(View c, Alignment alignment, int type, int size) {
-                    return max(0, super.getOffset(c, alignment, type, size));
+                protected int getOffset(View c, Alignment alignment, int size) {
+                    return max(0, super.getOffset(c, alignment, size));
                 }
             };
         }
@@ -2406,7 +2480,7 @@
      * {@link LayoutParams#columnGroup columnGroups}.
      */
     public static final Alignment FILL = new Alignment() {
-        public int getAlignmentValue(View view, int viewSize, int measurementType) {
+        public int getAlignmentValue(View view, int viewSize) {
             return UNDEFINED;
         }
 
@@ -2415,4 +2489,41 @@
             return cellSize;
         }
     };
+
+    /**
+     * Spec's tell GridLayout how to derive minimum and maximum size values for a
+     * component. Specifications are made with respect to a child's 'measured size'.
+     * A child's measured size is, in turn, controlled by its height and width
+     * layout parameters which either specify a size or, in the case of
+     * WRAP_CONTENT, defer to the computed size of the component.
+     */
+    public static abstract class Spec {
+    }
+
+    /**
+     * Indicates that a view requests precisely the size specified by its layout parameters.
+     *
+     * @see Spec
+     */
+    public static final Spec FIXED = new Spec() {
+    };
+
+    /**
+     * Indicates that a view's size should lie between its minimum and the size specified by
+     * its layout parameters.
+     *
+     * @see Spec
+     */
+    public static final Spec CAN_SHRINK = new Spec() {
+    };
+
+    /**
+     * Indicates that a view's size should be greater than or equal to the size specified by
+     * its layout parameters.
+     *
+     * @see Spec
+     */
+    public static final Spec CAN_STRETCH = new Spec() {
+    };
+
 }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 939779f..6154b42 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -10104,6 +10104,9 @@
             case TEXT_DIRECTION_ANY_RTL:
                 resolvedTextDirection = getTextDirectionFromAnyRtl(mText);
                 break;
+            case TEXT_DIRECTION_CHAR_COUNT:
+                resolvedTextDirection = getTextDirectionFromCharCount(mText);
+                break;
             case TEXT_DIRECTION_LTR:
                 resolvedTextDirection = TEXT_DIRECTION_LTR;
                 break;
@@ -10137,6 +10140,9 @@
      */
     private static int getTextDirectionFromFirstStrong(final CharSequence cs) {
         final int length = cs.length();
+        if (length == 0) {
+            return TEXT_DIRECTION_UNDEFINED;
+        }
         for(int i = 0; i < length; i++) {
             final char c = cs.charAt(i);
             final byte dir = Character.getDirectionality(c);
@@ -10159,6 +10165,9 @@
      */
     private static int getTextDirectionFromAnyRtl(final CharSequence cs) {
         final int length = cs.length();
+        if (length == 0) {
+            return TEXT_DIRECTION_UNDEFINED;
+        }
         boolean foundStrongLtr = false;
         boolean foundStrongRtl = false;
         for(int i = 0; i < length; i++) {
@@ -10180,6 +10189,41 @@
     }
 
     /**
+     * Get text direction following the "char count" heuristic.
+     *
+     * @param cs the CharSequence used to get the text direction.
+     *
+     * @return {@link #TEXT_DIRECTION_RTL} if direction it RTL, {@link #TEXT_DIRECTION_LTR} if
+     * direction it LTR or {@link #TEXT_DIRECTION_UNDEFINED} if direction cannot be found.
+     */
+    private int getTextDirectionFromCharCount(CharSequence cs) {
+        final int length = cs.length();
+        if (length == 0) {
+            return TEXT_DIRECTION_UNDEFINED;
+        }
+        int countLtr = 0;
+        int countRtl = 0;
+        for(int i = 0; i < length; i++) {
+            final char c = cs.charAt(i);
+            final byte dir = Character.getDirectionality(c);
+            if (isStrongLtrChar(dir)) {
+                countLtr++;
+            } else if (isStrongRtlChar(dir)) {
+                countRtl++;
+            }
+        }
+        final float percentLtr = ((float) countLtr) / (countLtr + countRtl);
+        if (percentLtr > DEFAULT_TEXT_DIRECTION_CHAR_COUNT_THRESHOLD) {
+            return TEXT_DIRECTION_LTR;
+        }
+        final float percentRtl = ((float) countRtl) / (countLtr + countRtl);
+        if (percentRtl > DEFAULT_TEXT_DIRECTION_CHAR_COUNT_THRESHOLD) {
+            return TEXT_DIRECTION_RTL;
+        }
+        return TEXT_DIRECTION_UNDEFINED;
+    }
+
+    /**
      * Return true if the char direction is corresponding to a "strong RTL char" following the
      * Unicode Bidirectional Algorithm (UBA).
      */
diff --git a/core/res/res/layout/keyguard_screen_password_landscape.xml b/core/res/res/layout/keyguard_screen_password_landscape.xml
index 8ba08f6..30df91b 100644
--- a/core/res/res/layout/keyguard_screen_password_landscape.xml
+++ b/core/res/res/layout/keyguard_screen_password_landscape.xml
@@ -156,7 +156,7 @@
     />
 
     <!-- Column 1 -->
-    <Space android:layout_columnWeight="1" android:layout_rowSpan="11" />
+    <Space android:layout_widthSpec="canStretch" android:layout_rowSpan="11" />
 
     <!-- Column 2 - password entry field and PIN keyboard -->
     <LinearLayout
diff --git a/core/res/res/layout/keyguard_screen_tab_unlock_land.xml b/core/res/res/layout/keyguard_screen_tab_unlock_land.xml
index 5588adc..c859720 100644
--- a/core/res/res/layout/keyguard_screen_tab_unlock_land.xml
+++ b/core/res/res/layout/keyguard_screen_tab_unlock_land.xml
@@ -169,7 +169,10 @@
     </LinearLayout>
 
     <!-- Column 1 -->
-    <Space android:width="20dip" android:layout_columnWeight="1" android:layout_rowSpan="10" />
+    <Space
+        android:width="20dip"
+        android:layout_heightSpec="canStretch"
+        android:layout_rowSpan="10" />
 
     <!-- Column 2 -->
     <com.android.internal.widget.multiwaveview.MultiWaveView
diff --git a/core/res/res/layout/keyguard_screen_unlock_landscape.xml b/core/res/res/layout/keyguard_screen_unlock_landscape.xml
index d0538dd..0070ed0 100644
--- a/core/res/res/layout/keyguard_screen_unlock_landscape.xml
+++ b/core/res/res/layout/keyguard_screen_unlock_landscape.xml
@@ -100,9 +100,11 @@
 
     <!-- TODO: remove hard coded height since layout_rowWeight doesn't seem to be working -->
     <Space
-    android:layout_height="43dip"
-    android:layout_gravity="fill"
-    android:layout_rowWeight="1" android:layout_columnWeight="1" />
+        android:layout_height="43dip"
+        android:layout_gravity="fill"
+        android:layout_heightSpec="canStretch"
+        android:layout_widthSpec="canStretch"
+        />
 
     <TextView android:id="@+id/carrier"
         android:layout_gravity="right"
diff --git a/core/res/res/layout/keyguard_screen_unlock_portrait.xml b/core/res/res/layout/keyguard_screen_unlock_portrait.xml
index 774f830..28c5302 100644
--- a/core/res/res/layout/keyguard_screen_unlock_portrait.xml
+++ b/core/res/res/layout/keyguard_screen_unlock_portrait.xml
@@ -125,7 +125,7 @@
         android:layout_marginBottom="4dip"
         android:layout_marginLeft="8dip"
         android:layout_gravity="center|bottom"
-        android:layout_rowWeight="1"
+        android:layout_heightSpec="canStretch"
      />
 
     <TextView
diff --git a/core/res/res/layout/search_view.xml b/core/res/res/layout/search_view.xml
index 475aa59..fee27eb 100644
--- a/core/res/res/layout/search_view.xml
+++ b/core/res/res/layout/search_view.xml
@@ -54,8 +54,8 @@
         android:layout_height="wrap_content"
         android:layout_weight="1"
         android:layout_gravity="center_vertical"
-        android:layout_marginLeft="16dip"
-        android:layout_marginRight="16dip"
+        android:layout_marginLeft="8dip"
+        android:layout_marginRight="8dip"
         android:layout_marginTop="4dip"
         android:layout_marginBottom="4dip"
         android:orientation="horizontal">
@@ -87,7 +87,6 @@
                 android:layout_gravity="bottom"
                 android:paddingLeft="8dip"
                 android:paddingRight="6dip"
-                android:drawablePadding="2dip"
                 android:singleLine="true"
                 android:ellipsize="end"
                 android:background="@null"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index db33d1c..e9b845d 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1985,10 +1985,14 @@
                  it is LTR if it contains any strong LTR characters.  If there are neither, the
                  paragraph direction is the view’s resolved layout direction. -->
             <enum name="anyRtl" value="2" />
-            <!-- The text direction is left to right. -->
-            <enum name="ltr" value="3" />
-            <!-- The text direction is right to left. -->
-            <enum name="rtl" value="4" />
+            <!-- The paragraph direction is the same as the one held by a 60% majority of the
+                 characters. If there is no majority then the paragraph direction is the resolved
+                 layout direction of the View. -->
+            <enum name="charCount" value="3" />
+            <!-- The paragraph direction is left to right. -->
+            <enum name="ltr" value="4" />
+            <!-- The paragraph direction is right to left. -->
+            <enum name="rtl" value="5" />
         </attr>
     </declare-styleable>
 
@@ -3324,11 +3328,6 @@
         The default is one.
         See {@link android.widget.GridLayout.Group}. -->
         <attr name="layout_rowSpan" format="integer" min="1" />
-        <!-- A number indicating the relative proportion of available space that
-        should be taken by this group of cells.
-        The default is zero.
-        See {@link android.widget.GridLayout.LayoutParams#columnWeight}. -->
-        <attr name="layout_rowWeight" format="float" />
         <!-- The column boundary delimiting the left of the group of cells
         occupied by this view. -->
         <attr name="layout_column" />
@@ -3337,15 +3336,38 @@
         The default is one.
         See {@link android.widget.GridLayout.Group}. -->
         <attr name="layout_columnSpan" format="integer" min="1" />
-        <!-- A number indicating the relative proportion of available space that
-        should be taken by this group of cells.
-        The default is zero.
-        See {@link android.widget.GridLayout.LayoutParams#columnWeight}.-->
-        <attr name="layout_columnWeight" format="float" />
         <!-- Gravity specifies how a component should be placed in its group of cells.
         The default is LEFT | BASELINE.
         See {@link android.widget.GridLayout.LayoutParams#setGravity(int)}. -->
         <attr name="layout_gravity" />
+        <!-- A value specifying how much deficit or excess width this component can accomodate.
+        The default is FIXED.
+        See {@link android.widget.GridLayout.LayoutParams#widthSpec}.-->
+        <attr name="layout_widthSpec" >
+            <!-- If possible, width should be exactly as specified.
+            See {@link android.widget.GridLayout#FIXED}. -->
+            <enum name="fixed" value="0" />
+            <!-- If possible, width should be less than or equal to the specified width.
+            See {@link android.widget.GridLayout#CAN_SHRINK}. -->
+            <enum name="canShrink" value="1" />
+            <!-- If possible, width should be greater than or equal to the specified width.
+            See {@link android.widget.GridLayout#CAN_STRETCH}. -->
+            <enum name="canStretch" value="2" />
+        </attr>
+        <!-- A value specifying how much deficit or excess height this component can accomodate.
+        The default is FIXED.
+        See {@link android.widget.GridLayout.LayoutParams#heightSpec}.-->
+        <attr name="layout_heightSpec" >
+            <!-- If possible, height should be exactly as specified.
+            See {@link android.widget.GridLayout#FIXED}. -->
+            <enum name="fixed" value="0" />
+            <!-- If possible, height should be less than or equal to the specified height.
+            See {@link android.widget.GridLayout#CAN_SHRINK}. -->
+            <enum name="canShrink" value="1" />
+            <!-- If possible, height should be greater than or equal to the specified height.
+            See {@link android.widget.GridLayout#CAN_STRETCH}. -->
+            <enum name="canStretch" value="2" />
+        </attr>
     </declare-styleable>
     <declare-styleable name="FrameLayout_Layout">
         <attr name="layout_gravity" />
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index db6f98f..945e0c4 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1738,9 +1738,11 @@
   <public type="attr" name="layout_row" />
   <public type="attr" name="layout_rowSpan" />
 
-  <public type="attr" name="layout_rowWeight" />
   <public type="attr" name="layout_columnSpan" />
-  <public type="attr" name="layout_columnWeight" />
+
+  <public type="attr" name="layout_widthSpec" />
+  <public type="attr" name="layout_heightSpec" />
+
   <public type="attr" name="actionModeSelectAllDrawable" />
 
   <public type="attr" name="isAuxiliary" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 29fca74..c8b3b4f 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2625,10 +2625,14 @@
     <!-- USB_STORAGE_ERROR dialog  ok button-->
     <string name="dlg_ok">OK</string>
 
-    <!-- USB_PREFERENCES: When the user connects the phone to a computer via USB, we show a notification asking if he wants to share files across.  This is the title -->
-    <string name="usb_preferences_notification_title">USB connected</string>
+    <!-- USB_PREFERENCES: Notification for wehen the user connects the phone to a computer via USB in MTP mode.  This is the title -->
+    <string name="usb_mtp_notification_title">Connected as a media device</string>
+    <!-- USB_PREFERENCES: Notification for wehen the user connects the phone to a computer via USB in PTP mode.  This is the title -->
+    <string name="usb_ptp_notification_title">Connected as a camera</string>
+    <!-- USB_PREFERENCES: Notification for wehen the user connects the phone to a computer via USB in mass storage mode (for installer CD image).  This is the title -->
+    <string name="usb_cd_installer_notification_title">Connected as an installer</string>
     <!-- See USB_PREFERENCES. This is the message. -->
-    <string name="usb_preferece_notification_message">Select to configure USB file transfer.</string>
+    <string name="usb_notification_message">Touch for other USB options</string>
 
     <!-- External media format dialog strings -->
     <!-- This is the label for the activity, and should never be visible to the user. -->
diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java
index a37f1a3..6db67c0 100644
--- a/core/tests/coretests/src/android/widget/TextViewTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewTest.java
@@ -177,4 +177,38 @@
         tv.setTextDirection(View.TEXT_DIRECTION_RTL);
         assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
     }
+
+    @SmallTest
+    public void testCharCountHeuristic() {
+        LinearLayout ll = new LinearLayout(mContext);
+        ll.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
+
+        TextView tv = new TextView(mContext);
+        ll.addView(tv);
+
+        tv.setTextDirection(View.TEXT_DIRECTION_CHAR_COUNT);
+        tv.setText("this is a test");
+        assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection());
+
+         // resetResolvedTextDirection is not part of the public API so simply use setTextDirection
+        tv.setTextDirection(View.TEXT_DIRECTION_LTR);
+        tv.setTextDirection(View.TEXT_DIRECTION_CHAR_COUNT);
+        tv.setText("\u05DD\u05DE"); // hebrew
+        assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_LTR);
+        tv.setTextDirection(View.TEXT_DIRECTION_CHAR_COUNT);
+        tv.setText("this is a test \u05DD\u05DE"); // latin more than 60% + hebrew
+        assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_LTR);
+        tv.setTextDirection(View.TEXT_DIRECTION_CHAR_COUNT);
+        tv.setText("t \u05DD\u05DE"); // latin + hebrew more than 60%
+        assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_LTR);
+        tv.setTextDirection(View.TEXT_DIRECTION_CHAR_COUNT);
+        tv.setText("ab \u05DD\u05DE"); // latin + hebrew at 50% each
+        assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
+    }
 }
diff --git a/include/media/stagefright/MediaSource.h b/include/media/stagefright/MediaSource.h
index a31395e..37dbcd8 100644
--- a/include/media/stagefright/MediaSource.h
+++ b/include/media/stagefright/MediaSource.h
@@ -22,6 +22,7 @@
 
 #include <media/stagefright/MediaErrors.h>
 #include <utils/RefBase.h>
+#include <utils/Vector.h>
 
 namespace android {
 
@@ -99,6 +100,15 @@
         return ERROR_UNSUPPORTED;
     }
 
+    // The consumer of this media source requests that the given buffers
+    // are to be returned exclusively in response to read calls.
+    // This will be called after a successful start() and before the
+    // first read() call.
+    // Callee assumes ownership of the buffers if no error is returned.
+    virtual status_t setBuffers(const Vector<MediaBuffer *> &buffers) {
+        return ERROR_UNSUPPORTED;
+    }
+
 protected:
     virtual ~MediaSource();
 
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index 99b72ad..57f678c 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -121,6 +121,8 @@
 
     // To store the timed text format data
     kKeyTextFormatData    = 'text',  // raw data
+
+    kKeyRequiresSecureBuffers = 'secu',  // bool (int32_t)
 };
 
 enum {
diff --git a/include/media/stagefright/MetadataBufferType.h b/include/media/stagefright/MetadataBufferType.h
new file mode 100644
index 0000000..275c19f
--- /dev/null
+++ b/include/media/stagefright/MetadataBufferType.h
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+#ifndef METADATA_BUFFER_TYPE_H
+#define METADATA_BUFFER_TYPE_H
+
+namespace android {
+/*
+ * MetadataBufferType defines the type of the metadata buffers that
+ * can be passed to video encoder component for encoding, via Stagefright
+ * media recording framework. To see how to work with the metadata buffers
+ * in media recording framework, please consult HardwareAPI.h
+ *
+ * The creator of metadata buffers and video encoder share common knowledge
+ * on what is actually being stored in these metadata buffers, and
+ * how the information can be used by the video encoder component
+ * to locate the actual pixel data as the source input for video
+ * encoder, plus whatever other information that is necessary. Stagefright
+ * media recording framework does not need to know anything specific about the
+ * metadata buffers, except for receving each individual metadata buffer
+ * as the source input, making a copy of the metadata buffer, and passing the
+ * copy via OpenMAX API to the video encoder component.
+ *
+ * The creator of the metadata buffers must ensure that the first
+ * 4 bytes in every metadata buffer indicates its buffer type,
+ * and the rest of the metadata buffer contains the
+ * actual metadata information. When a video encoder component receives
+ * a metadata buffer, it uses the first 4 bytes in that buffer to find
+ * out the type of the metadata buffer, and takes action appropriate
+ * to that type of metadata buffers (for instance, locate the actual
+ * pixel data input and then encoding the input data to produce a
+ * compressed output buffer).
+ *
+ * The following shows the layout of a metadata buffer,
+ * where buffer type is a 4-byte field of MetadataBufferType,
+ * and the payload is the metadata information.
+ *
+ * --------------------------------------------------------------
+ * |  buffer type  |          payload                           |
+ * --------------------------------------------------------------
+ *
+ */
+typedef enum {
+
+    /*
+     * kMetadataBufferTypeCameraSource is used to indicate that
+     * the source of the metadata buffer is the camera component.
+     */
+    kMetadataBufferTypeCameraSource  = 0,
+
+    /*
+     * kMetadataBufferTypeGrallocSource is used to indicate that
+     * the payload of the metadata buffers can be interpreted as
+     * a buffer_handle_t.
+     */
+    kMetadataBufferTypeGrallocSource = 1,
+
+    // Add more here...
+
+} MetadataBufferType;
+
+}  // namespace android
+
+#endif  // METADATA_BUFFER_TYPE_H
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index 92331a1..7f3c497 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -53,6 +53,9 @@
 
         // Enable GRALLOC_USAGE_PROTECTED for output buffers from native window
         kEnableGrallocUsageProtected = 128,
+
+        // Secure decoding mode
+        kUseSecureInputBuffers = 256,
     };
     static sp<MediaSource> Create(
             const sp<IOMX> &omx,
@@ -164,6 +167,10 @@
     bool mOMXLivesLocally;
     IOMX::node_id mNode;
     uint32_t mQuirks;
+
+    // Flags specified in the creation of the codec.
+    uint32_t mFlags;
+
     bool mIsEncoder;
     char *mMIME;
     char *mComponentName;
@@ -205,15 +212,12 @@
     List<size_t> mFilledBuffers;
     Condition mBufferFilled;
 
-    bool mIsMetaDataStoredInVideoBuffers;
-    bool mOnlySubmitOneBufferAtOneTime;
-    bool mEnableGrallocUsageProtected;
-
     // Used to record the decoding time for an output picture from
     // a video encoder.
     List<int64_t> mDecodingTimeList;
 
-    OMXCodec(const sp<IOMX> &omx, IOMX::node_id node, uint32_t quirks,
+    OMXCodec(const sp<IOMX> &omx, IOMX::node_id node,
+             uint32_t quirks, uint32_t flags,
              bool isEncoder, const char *mime, const char *componentName,
              const sp<MediaSource> &source,
              const sp<ANativeWindow> &nativeWindow);
@@ -287,6 +291,10 @@
     void drainInputBuffers();
     void fillOutputBuffers();
 
+    bool drainAnyInputBuffer();
+    BufferInfo *findInputBufferByDataPointer(void *ptr);
+    BufferInfo *findEmptyInputBuffer();
+
     // Returns true iff a flush was initiated and a completion event is
     // upcoming, false otherwise (A flush was not necessary as we own all
     // the buffers on that port).
@@ -313,7 +321,7 @@
 
     void dumpPortStatus(OMX_U32 portIndex);
 
-    status_t configureCodec(const sp<MetaData> &meta, uint32_t flags);
+    status_t configureCodec(const sp<MetaData> &meta);
 
     static uint32_t getComponentQuirks(
             const char *componentName, bool isEncoder);
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 167071a3..d4d07b2 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -1190,6 +1190,17 @@
             CHECK(msg->findInt32("data1", &data1));
             CHECK(msg->findInt32("data2", &data2));
 
+            if (event == OMX_EventCmdComplete
+                    && data1 == OMX_CommandFlush
+                    && data2 == (int32_t)OMX_ALL) {
+                // Use of this notification is not consistent across
+                // implementations. We'll drop this notification and rely
+                // on flush-complete notifications on the individual port
+                // indices instead.
+
+                return true;
+            }
+
             return onOMXEvent(
                     static_cast<OMX_EVENTTYPE>(event),
                     static_cast<OMX_U32>(data1),
@@ -2119,6 +2130,7 @@
             return BaseState::onOMXEvent(event, data1, data2);
     }
 }
+
 void ACodec::ExecutingToIdleState::changeStateIfWeOwnAllBuffers() {
     if (mCodec->allYourBuffersAreBelongToUs()) {
         CHECK_EQ(mCodec->mOMX->sendCommand(
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index e36b01f..1ac2c1f 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -477,6 +477,15 @@
         const char *matchComponentName,
         uint32_t flags,
         const sp<ANativeWindow> &nativeWindow) {
+    int32_t requiresSecureBuffers;
+    if (source->getFormat()->findInt32(
+                kKeyRequiresSecureBuffers,
+                &requiresSecureBuffers)
+            && requiresSecureBuffers) {
+        flags |= kIgnoreCodecSpecificData;
+        flags |= kUseSecureInputBuffers;
+    }
+
     const char *mime;
     bool success = meta->findCString(kKeyMIMEType, &mime);
     CHECK(success);
@@ -530,17 +539,17 @@
             LOGV("Successfully allocated OMX node '%s'", componentName);
 
             sp<OMXCodec> codec = new OMXCodec(
-                    omx, node, quirks,
+                    omx, node, quirks, flags,
                     createEncoder, mime, componentName,
                     source, nativeWindow);
 
             observer->setCodec(codec);
 
-            err = codec->configureCodec(meta, flags);
+            err = codec->configureCodec(meta);
 
             if (err == OK) {
                 if (!strcmp("OMX.Nvidia.mpeg2v.decode", componentName)) {
-                    codec->mOnlySubmitOneBufferAtOneTime = true;
+                    codec->mFlags |= kOnlySubmitOneInputBufferAtOneTime;
                 }
 
                 return codec;
@@ -553,24 +562,11 @@
     return NULL;
 }
 
-status_t OMXCodec::configureCodec(const sp<MetaData> &meta, uint32_t flags) {
-    mIsMetaDataStoredInVideoBuffers = false;
-    if (flags & kStoreMetaDataInVideoBuffers) {
-        mIsMetaDataStoredInVideoBuffers = true;
-    }
+status_t OMXCodec::configureCodec(const sp<MetaData> &meta) {
+    LOGV("configureCodec protected=%d",
+         (mFlags & kEnableGrallocUsageProtected) ? 1 : 0);
 
-    mOnlySubmitOneBufferAtOneTime = false;
-    if (flags & kOnlySubmitOneInputBufferAtOneTime) {
-        mOnlySubmitOneBufferAtOneTime = true;
-    }
-
-    mEnableGrallocUsageProtected = false;
-    if (flags & kEnableGrallocUsageProtected) {
-        mEnableGrallocUsageProtected = true;
-    }
-    LOGV("configureCodec protected=%d", mEnableGrallocUsageProtected);
-
-    if (!(flags & kIgnoreCodecSpecificData)) {
+    if (!(mFlags & kIgnoreCodecSpecificData)) {
         uint32_t type;
         const void *data;
         size_t size;
@@ -745,7 +741,7 @@
 
     initOutputFormat(meta);
 
-    if ((flags & kClientNeedsFramebuffer)
+    if ((mFlags & kClientNeedsFramebuffer)
             && !strncmp(mComponentName, "OMX.SEC.", 8)) {
         OMX_INDEXTYPE index;
 
@@ -1468,7 +1464,8 @@
 }
 
 OMXCodec::OMXCodec(
-        const sp<IOMX> &omx, IOMX::node_id node, uint32_t quirks,
+        const sp<IOMX> &omx, IOMX::node_id node,
+        uint32_t quirks, uint32_t flags,
         bool isEncoder,
         const char *mime,
         const char *componentName,
@@ -1478,6 +1475,7 @@
       mOMXLivesLocally(omx->livesLocally(getpid())),
       mNode(node),
       mQuirks(quirks),
+      mFlags(flags),
       mIsEncoder(isEncoder),
       mMIME(strdup(mime)),
       mComponentName(strdup(componentName)),
@@ -1645,13 +1643,14 @@
         return allocateOutputBuffersFromNativeWindow();
     }
 
-    if (mEnableGrallocUsageProtected && portIndex == kPortIndexOutput) {
+    if ((mFlags & kEnableGrallocUsageProtected) && portIndex == kPortIndexOutput) {
         LOGE("protected output buffers must be stent to an ANativeWindow");
         return PERMISSION_DENIED;
     }
 
     status_t err = OK;
-    if (mIsMetaDataStoredInVideoBuffers && portIndex == kPortIndexInput) {
+    if ((mFlags & kStoreMetaDataInVideoBuffers)
+            && portIndex == kPortIndexInput) {
         err = mOMX->storeMetaDataInBuffers(mNode, kPortIndexInput, OMX_TRUE);
         if (err != OK) {
             LOGE("Storing meta data in video buffers is not supported");
@@ -1687,7 +1686,8 @@
 
         IOMX::buffer_id buffer;
         if (portIndex == kPortIndexInput
-                && (mQuirks & kRequiresAllocateBufferOnInputPorts)) {
+                && ((mQuirks & kRequiresAllocateBufferOnInputPorts)
+                    || (mFlags & kUseSecureInputBuffers))) {
             if (mOMXLivesLocally) {
                 mem.clear();
 
@@ -1748,6 +1748,31 @@
 
     // dumpPortStatus(portIndex);
 
+    if (portIndex == kPortIndexInput && (mFlags & kUseSecureInputBuffers)) {
+        Vector<MediaBuffer *> buffers;
+        for (size_t i = 0; i < def.nBufferCountActual; ++i) {
+            const BufferInfo &info = mPortBuffers[kPortIndexInput].itemAt(i);
+
+            MediaBuffer *mbuf = new MediaBuffer(info.mData, info.mSize);
+            buffers.push(mbuf);
+        }
+
+        status_t err = mSource->setBuffers(buffers);
+
+        if (err != OK) {
+            for (size_t i = 0; i < def.nBufferCountActual; ++i) {
+                buffers.editItemAt(i)->release();
+            }
+            buffers.clear();
+
+            CODEC_LOGE(
+                    "Codec requested to use secure input buffers but "
+                    "upstream source didn't support that.");
+
+            return err;
+        }
+    }
+
     return OK;
 }
 
@@ -1815,7 +1840,7 @@
         // XXX: Currently this error is logged, but not fatal.
         usage = 0;
     }
-    if (mEnableGrallocUsageProtected) {
+    if (mFlags & kEnableGrallocUsageProtected) {
         usage |= GRALLOC_USAGE_PROTECTED;
     }
 
@@ -2067,7 +2092,12 @@
             } else if (mState != ERROR
                     && mPortStatus[kPortIndexInput] != SHUTTING_DOWN) {
                 CHECK_EQ((int)mPortStatus[kPortIndexInput], (int)ENABLED);
-                drainInputBuffer(&buffers->editItemAt(i));
+
+                if (mFlags & kUseSecureInputBuffers) {
+                    drainAnyInputBuffer();
+                } else {
+                    drainInputBuffer(&buffers->editItemAt(i));
+                }
             }
             break;
         }
@@ -2804,32 +2834,81 @@
 void OMXCodec::drainInputBuffers() {
     CHECK(mState == EXECUTING || mState == RECONFIGURING);
 
-    Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexInput];
-    for (size_t i = 0; i < buffers->size(); ++i) {
-        BufferInfo *info = &buffers->editItemAt(i);
-
-        if (info->mStatus != OWNED_BY_US) {
-            continue;
+    if (mFlags & kUseSecureInputBuffers) {
+        Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexInput];
+        for (size_t i = 0; i < buffers->size(); ++i) {
+            if (!drainAnyInputBuffer()
+                    || (mFlags & kOnlySubmitOneInputBufferAtOneTime)) {
+                break;
+            }
         }
+    } else {
+        Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexInput];
+        for (size_t i = 0; i < buffers->size(); ++i) {
+            BufferInfo *info = &buffers->editItemAt(i);
 
-        if (!drainInputBuffer(info)) {
-            break;
-        }
+            if (info->mStatus != OWNED_BY_US) {
+                continue;
+            }
 
-        if (mOnlySubmitOneBufferAtOneTime) {
-            break;
+            if (!drainInputBuffer(info)) {
+                break;
+            }
+
+            if (mFlags & kOnlySubmitOneInputBufferAtOneTime) {
+                break;
+            }
         }
     }
 }
 
+bool OMXCodec::drainAnyInputBuffer() {
+    return drainInputBuffer((BufferInfo *)NULL);
+}
+
+OMXCodec::BufferInfo *OMXCodec::findInputBufferByDataPointer(void *ptr) {
+    Vector<BufferInfo> *infos = &mPortBuffers[kPortIndexInput];
+    for (size_t i = 0; i < infos->size(); ++i) {
+        BufferInfo *info = &infos->editItemAt(i);
+
+        if (info->mData == ptr) {
+            CODEC_LOGV(
+                    "input buffer data ptr = %p, buffer_id = %p",
+                    ptr,
+                    info->mBuffer);
+
+            return info;
+        }
+    }
+
+    TRESPASS();
+}
+
+OMXCodec::BufferInfo *OMXCodec::findEmptyInputBuffer() {
+    Vector<BufferInfo> *infos = &mPortBuffers[kPortIndexInput];
+    for (size_t i = 0; i < infos->size(); ++i) {
+        BufferInfo *info = &infos->editItemAt(i);
+
+        if (info->mStatus == OWNED_BY_US) {
+            return info;
+        }
+    }
+
+    TRESPASS();
+}
+
 bool OMXCodec::drainInputBuffer(BufferInfo *info) {
-    CHECK_EQ((int)info->mStatus, (int)OWNED_BY_US);
+    if (info != NULL) {
+        CHECK_EQ((int)info->mStatus, (int)OWNED_BY_US);
+    }
 
     if (mSignalledEOS) {
         return false;
     }
 
     if (mCodecSpecificDataIndex < mCodecSpecificData.size()) {
+        CHECK(!(mFlags & kUseSecureInputBuffers));
+
         const CodecSpecificData *specific =
             mCodecSpecificData[mCodecSpecificDataIndex];
 
@@ -2925,6 +3004,11 @@
             break;
         }
 
+        if (mFlags & kUseSecureInputBuffers) {
+            info = findInputBufferByDataPointer(srcBuffer->data());
+            CHECK(info != NULL);
+        }
+
         size_t remainingBytes = info->mSize - offset;
 
         if (srcBuffer->range_length() > remainingBytes) {
@@ -2960,14 +3044,24 @@
             releaseBuffer = false;
             info->mMediaBuffer = srcBuffer;
         } else {
-            if (mIsMetaDataStoredInVideoBuffers) {
+            if (mFlags & kStoreMetaDataInVideoBuffers) {
                 releaseBuffer = false;
                 info->mMediaBuffer = srcBuffer;
             }
-            memcpy((uint8_t *)info->mData + offset,
-                    (const uint8_t *)srcBuffer->data()
-                        + srcBuffer->range_offset(),
-                    srcBuffer->range_length());
+
+            if (mFlags & kUseSecureInputBuffers) {
+                // Data in "info" is already provided at this time.
+
+                releaseBuffer = false;
+
+                CHECK(info->mMediaBuffer == NULL);
+                info->mMediaBuffer = srcBuffer;
+            } else {
+                memcpy((uint8_t *)info->mData + offset,
+                        (const uint8_t *)srcBuffer->data()
+                            + srcBuffer->range_offset(),
+                        srcBuffer->range_length());
+            }
         }
 
         int64_t lastBufferTimeUs;
@@ -3036,6 +3130,16 @@
                info->mBuffer, offset,
                timestampUs, timestampUs / 1E6);
 
+    if (info == NULL) {
+        CHECK(mFlags & kUseSecureInputBuffers);
+        CHECK(signalEOS);
+
+        // This is fishy, there's still a MediaBuffer corresponding to this
+        // info available to the source at this point even though we're going
+        // to use it to signal EOS to the codec.
+        info = findEmptyInputBuffer();
+    }
+
     err = mOMX->emptyBuffer(
             mNode, info->mBuffer, 0, offset,
             flags, timestampUs);
diff --git a/media/libstagefright/WVMExtractor.cpp b/media/libstagefright/WVMExtractor.cpp
index 7072d58..26eda0c 100644
--- a/media/libstagefright/WVMExtractor.cpp
+++ b/media/libstagefright/WVMExtractor.cpp
@@ -33,25 +33,26 @@
 
 #include <utils/Errors.h>
 
+/* The extractor lifetime is short - just long enough to get
+ * the media sources constructed - so the shared lib needs to remain open
+ * beyond the lifetime of the extractor.  So keep the handle as a global
+ * rather than a member of the extractor
+ */
+void *gVendorLibHandle = NULL;
+
 namespace android {
 
-Mutex WVMExtractor::sMutex;
-uint32_t WVMExtractor::sActiveExtractors = 0;
-void *WVMExtractor::sVendorLibHandle = NULL;
+static Mutex gWVMutex;
 
 WVMExtractor::WVMExtractor(const sp<DataSource> &source)
     : mDataSource(source) {
     {
-        Mutex::Autolock autoLock(sMutex);
-
-        if (sVendorLibHandle == NULL) {
-            CHECK(sActiveExtractors == 0);
-            sVendorLibHandle = dlopen("libwvm.so", RTLD_NOW);
+        Mutex::Autolock autoLock(gWVMutex);
+        if (gVendorLibHandle == NULL) {
+            gVendorLibHandle = dlopen("libwvm.so", RTLD_NOW);
         }
 
-        sActiveExtractors++;
-
-        if (sVendorLibHandle == NULL) {
+        if (gVendorLibHandle == NULL) {
             LOGE("Failed to open libwvm.so");
             return;
         }
@@ -59,7 +60,7 @@
 
     typedef WVMLoadableExtractor *(*GetInstanceFunc)(sp<DataSource>);
     GetInstanceFunc getInstanceFunc =
-        (GetInstanceFunc) dlsym(sVendorLibHandle,
+        (GetInstanceFunc) dlsym(gVendorLibHandle,
                 "_ZN7android11GetInstanceENS_2spINS_10DataSourceEEE");
 
     if (getInstanceFunc) {
@@ -71,17 +72,6 @@
 }
 
 WVMExtractor::~WVMExtractor() {
-    Mutex::Autolock autoLock(sMutex);
-
-    CHECK(sActiveExtractors > 0);
-    sActiveExtractors--;
-
-    // Close lib after last use
-    if (sActiveExtractors == 0) {
-        if (sVendorLibHandle != NULL)
-            dlclose(sVendorLibHandle);
-        sVendorLibHandle = NULL;
-    }
 }
 
 size_t WVMExtractor::countTracks() {
diff --git a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
index e3292e6..0096760 100644
--- a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
+++ b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
@@ -475,7 +475,9 @@
         }
         status_t err = mSource->read(&mInputBuffer, options);
         if (err != OK) {
-            LOGE("Failed to read input video frame: %d", err);
+            if (err != ERROR_END_OF_STREAM) {
+                LOGE("Failed to read input video frame: %d", err);
+            }
             outputBuffer->release();
             return err;
         }
diff --git a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
index 15ed219..d7249c1 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
+++ b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
@@ -398,10 +398,13 @@
     }
 
     // Ready for accepting an input video frame
-    if (OK != mSource->read(&mInputBuffer, options)) {
-        LOGE("Failed to read from data source");
+    status_t err = mSource->read(&mInputBuffer, options);
+    if (OK != err) {
+        if (err != ERROR_END_OF_STREAM) {
+            LOGE("Failed to read from data source");
+        }
         outputBuffer->release();
-        return UNKNOWN_ERROR;
+        return err;
     }
 
     if (mInputBuffer->size() - ((mVideoWidth * mVideoHeight * 3) >> 1) != 0) {
diff --git a/media/libstagefright/include/WVMExtractor.h b/media/libstagefright/include/WVMExtractor.h
index 0817bab..deecd25 100644
--- a/media/libstagefright/include/WVMExtractor.h
+++ b/media/libstagefright/include/WVMExtractor.h
@@ -18,7 +18,6 @@
 
 #define WVM_EXTRACTOR_H_
 
-#include <media/stagefright/DataSource.h>
 #include <media/stagefright/MediaExtractor.h>
 #include <utils/Errors.h>
 
@@ -68,10 +67,6 @@
 
     WVMExtractor(const WVMExtractor &);
     WVMExtractor &operator=(const WVMExtractor &);
-
-    static Mutex sMutex;
-    static uint32_t sActiveExtractors;
-    static void *sVendorLibHandle;
 };
 
 }  // namespace android
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 6d8eab6..f42cbbf 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -25,6 +25,11 @@
             android:exported="true"
             />
 
+        <!-- started from PhoneWindowManager
+             TODO: Should have an android:permission attribute -->
+        <service android:name=".screenshot.TakeScreenshotService"
+            android:exported="false" />
+
         <activity android:name=".usb.UsbPreferenceActivity"
              android:theme="@*android:style/Theme.Holo.Dialog.Alert"
              android:excludeFromRecents="true">
diff --git a/packages/SystemUI/res/drawable-hdpi/global_screenshot_background.9.png b/packages/SystemUI/res/drawable-hdpi/global_screenshot_background.9.png
new file mode 100644
index 0000000..e14111d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/global_screenshot_background.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/global_screenshot_background.9.png b/packages/SystemUI/res/drawable-mdpi/global_screenshot_background.9.png
new file mode 100644
index 0000000..e14111d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/global_screenshot_background.9.png
Binary files differ
diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml
new file mode 100644
index 0000000..6cb8799
--- /dev/null
+++ b/packages/SystemUI/res/layout/global_screenshot.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <ImageView android:id="@+id/global_screenshot_background"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="#FF000000"
+        android:visibility="gone" />
+    <FrameLayout
+        android:id="@+id/global_screenshot_container"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:background="@drawable/global_screenshot_background"
+        android:visibility="gone">
+        <ImageView android:id="@+id/global_screenshot"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:adjustViewBounds="true" />
+    </FrameLayout>
+    <ImageView android:id="@+id/global_screenshot_flash"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="#FFFFFFFF"
+        android:visibility="gone" />
+</FrameLayout>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 86e0cd0..70f9b75 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -165,4 +165,9 @@
     <string name="use_ptp_button_title">Mount as a camera (PTP)</string>
     <!-- Label for the installer CD image option in UsbPreferenceActivity. [CHAR LIMIT=50] -->
     <string name="installer_cd_button_title">Install Android File Transfer application for Mac</string>
+
+    <!-- toast message displayed when a screenshot is saved to the Gallery. -->
+    <string name="screenshot_saving_toast">Screenshot saved to Gallery</string>
+    <!-- toast message displayed when we fail to take a screenshot. -->
+    <string name="screenshot_failed_toast">Could not save screenshot</string>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
new file mode 100644
index 0000000..83a5578
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -0,0 +1,384 @@
+/*
+ * 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.systemui.screenshot;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.app.Activity;
+import android.content.ContentValues;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.PixelFormat;
+import android.media.MediaScannerConnection;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Binder;
+import android.os.Environment;
+import android.os.ServiceManager;
+import android.provider.MediaStore;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Display;
+import android.view.IWindowManager;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.systemui.R;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.Thread;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * POD used in the AsyncTask which saves an image in the background.
+ */
+class SaveImageInBackgroundData {
+    Context context;
+    Bitmap image;
+    int result;
+}
+
+/**
+ * An AsyncTask that saves an image to the media store in the background.
+ */
+class SaveImageInBackgroundTask extends AsyncTask<SaveImageInBackgroundData, Void,
+        SaveImageInBackgroundData> {
+    private static final String TAG = "SaveImageInBackgroundTask";
+    private static final String SCREENSHOTS_DIR_NAME = "Screenshots";
+    private static final String SCREENSHOT_FILE_PATH_TEMPLATE = "%s/%s/Screenshot_%s-%d.png";
+
+    @Override
+    protected SaveImageInBackgroundData doInBackground(SaveImageInBackgroundData... params) {
+        if (params.length != 1) return null;
+
+        Context context = params[0].context;
+        Bitmap image = params[0].image;
+
+        try{
+            long currentTime = System.currentTimeMillis();
+            String date = new SimpleDateFormat("MM-dd-yy-kk-mm-ss").format(new Date(currentTime));
+            String imageDir = Environment.getExternalStoragePublicDirectory(
+                    Environment.DIRECTORY_PICTURES).getAbsolutePath();
+            String imageFilePath = String.format(SCREENSHOT_FILE_PATH_TEMPLATE,
+                    imageDir, SCREENSHOTS_DIR_NAME,
+                    date, currentTime % 1000);
+
+            // Save the screenshot to the MediaStore
+            ContentValues values = new ContentValues();
+            values.put(MediaStore.Images.ImageColumns.DATA, imageFilePath);
+            values.put(MediaStore.Images.ImageColumns.TITLE, "Screenshot");
+            values.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, "Screenshot");
+            values.put(MediaStore.Images.ImageColumns.DATE_TAKEN, currentTime);
+            values.put(MediaStore.Images.ImageColumns.DATE_ADDED, currentTime);
+            values.put(MediaStore.Images.ImageColumns.DATE_MODIFIED, currentTime);
+            values.put(MediaStore.Images.ImageColumns.MIME_TYPE, "image/png");
+            Uri uri = context.getContentResolver().insert(
+                        MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
+
+            OutputStream out = context.getContentResolver().openOutputStream(uri);
+            image.compress(Bitmap.CompressFormat.PNG, 100, out);
+            out.flush();
+            out.close();
+
+            params[0].result = 0;
+        }catch(IOException e){
+            params[0].result = 1;
+        }
+
+        return params[0];
+    };
+
+    @Override
+    protected void onPostExecute(SaveImageInBackgroundData params) {
+        if (params.result > 0) {
+            // Show a message that we've failed to save the image to disk
+            Toast.makeText(params.context, R.string.screenshot_failed_toast,
+                    Toast.LENGTH_SHORT).show();
+        } else {
+            // Show a message that we've saved the screenshot to disk
+            Toast.makeText(params.context, R.string.screenshot_saving_toast,
+                    Toast.LENGTH_SHORT).show();
+        }
+    };
+}
+
+/**
+ * TODO:
+ *   - Performance when over gl surfaces? Ie. Gallery
+ *   - what do we say in the Toast? Which icon do we get if the user uses another
+ *     type of gallery?
+ */
+class GlobalScreenshot {
+    private static final String TAG = "GlobalScreenshot";
+    private static final int SCREENSHOT_FADE_IN_DURATION = 900;
+    private static final int SCREENSHOT_FADE_OUT_DELAY = 1000;
+    private static final int SCREENSHOT_FADE_OUT_DURATION = 450;
+    private static final int TOAST_FADE_IN_DURATION = 500;
+    private static final int TOAST_FADE_OUT_DELAY = 1000;
+    private static final int TOAST_FADE_OUT_DURATION = 500;
+    private static final float BACKGROUND_ALPHA = 0.65f;
+    private static final float SCREENSHOT_SCALE = 0.85f;
+    private static final float SCREENSHOT_MIN_SCALE = 0.7f;
+    private static final float SCREENSHOT_ROTATION = -6.75f; // -12.5f;
+
+    private Context mContext;
+    private LayoutInflater mLayoutInflater;
+    private IWindowManager mIWindowManager;
+    private WindowManager mWindowManager;
+    private WindowManager.LayoutParams mWindowLayoutParams;
+    private Display mDisplay;
+    private DisplayMetrics mDisplayMetrics;
+    private Matrix mDisplayMatrix;
+
+    private Bitmap mScreenBitmap;
+    private View mScreenshotLayout;
+    private ImageView mBackgroundView;
+    private FrameLayout mScreenshotContainerView;
+    private ImageView mScreenshotView;
+
+    private AnimatorSet mScreenshotAnimation;
+
+    // General use cubic interpolator
+    final TimeInterpolator mCubicInterpolator = new TimeInterpolator() {
+        public float getInterpolation(float t) {
+            return t*t*t;
+        }
+    };
+    // The interpolator used to control the background alpha at the start of the animation
+    final TimeInterpolator mBackgroundViewAlphaInterpolator = new TimeInterpolator() {
+        public float getInterpolation(float t) {
+            float tStep = 0.35f;
+            if (t < tStep) {
+                return t * (1f / tStep);
+            } else {
+                return 1f;
+            }
+        }
+    };
+
+    /**
+     * @param context everything needs a context :(
+     */
+    public GlobalScreenshot(Context context) {
+        mContext = context;
+        mLayoutInflater = (LayoutInflater)
+                context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+        // Inflate the screenshot layout
+        mDisplayMetrics = new DisplayMetrics();
+        mDisplayMatrix = new Matrix();
+        mScreenshotLayout = mLayoutInflater.inflate(R.layout.global_screenshot, null);
+        mBackgroundView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot_background);
+        mScreenshotContainerView = (FrameLayout) mScreenshotLayout.findViewById(R.id.global_screenshot_container);
+        mScreenshotView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot);
+        mScreenshotLayout.setFocusable(true);
+        mScreenshotLayout.setOnTouchListener(new View.OnTouchListener() {
+            @Override
+            public boolean onTouch(View v, MotionEvent event) {
+                // Intercept and ignore all touch events
+                return true;
+            }
+        });
+
+        // Setup the window that we are going to use
+        mIWindowManager = IWindowManager.Stub.asInterface(
+                ServiceManager.getService(Context.WINDOW_SERVICE));
+        mWindowLayoutParams = new WindowManager.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 0, 0,
+                WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY,
+                WindowManager.LayoutParams.FLAG_FULLSCREEN
+                    | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
+                    | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED_SYSTEM
+                    | WindowManager.LayoutParams.FLAG_KEEP_SURFACE_WHILE_ANIMATING
+                    | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                    | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED,
+                PixelFormat.TRANSLUCENT);
+        mWindowLayoutParams.token = new Binder();
+        mWindowLayoutParams.setTitle("ScreenshotAnimation");
+        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+        mDisplay = mWindowManager.getDefaultDisplay();
+    }
+
+    /**
+     * Creates a new worker thread and saves the screenshot to the media store.
+     */
+    private void saveScreenshotInWorkerThread() {
+        SaveImageInBackgroundData data = new SaveImageInBackgroundData();
+        data.context = mContext;
+        data.image = mScreenBitmap;
+        new SaveImageInBackgroundTask().execute(data);
+    }
+
+    /**
+     * @return the current display rotation in degrees
+     */
+    private float getDegreesForRotation(int value) {
+        switch (value) {
+        case Surface.ROTATION_90:
+            return 90f;
+        case Surface.ROTATION_180:
+            return 180f;
+        case Surface.ROTATION_270:
+            return 270f;
+        }
+        return 0f;
+    }
+
+    /**
+     * Takes a screenshot of the current display and shows an animation.
+     */
+    void takeScreenshot() {
+        // We need to orient the screenshot correctly (and the Surface api seems to take screenshots
+        // only in the natural orientation of the device :!)
+        mDisplay.getRealMetrics(mDisplayMetrics);
+        float[] dims = {mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels};
+        float degrees = getDegreesForRotation(mDisplay.getRotation());
+        boolean requiresRotation = (degrees > 0);
+        if (requiresRotation) {
+            // Get the dimensions of the device in its native orientation
+            mDisplayMatrix.reset();
+            mDisplayMatrix.preRotate(-degrees);
+            mDisplayMatrix.mapPoints(dims);
+            dims[0] = Math.abs(dims[0]);
+            dims[1] = Math.abs(dims[1]);
+        }
+        mScreenBitmap = Surface.screenshot((int) dims[0], (int) dims[1]);
+        if (requiresRotation) {
+            // Rotate the screenshot to the current orientation
+            Bitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels,
+                    mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888);
+            Canvas c = new Canvas(ss);
+            c.translate(ss.getWidth() / 2, ss.getHeight() / 2);
+            c.rotate(360f - degrees);
+            c.translate(-dims[0] / 2, -dims[1] / 2);
+            c.drawBitmap(mScreenBitmap, 0, 0, null);
+            mScreenBitmap = ss;
+        }
+
+        // If we couldn't take the screenshot, notify the user
+        if (mScreenBitmap == null) {
+            Toast.makeText(mContext, R.string.screenshot_failed_toast,
+                    Toast.LENGTH_SHORT).show();
+            return;
+        }
+
+        // Start the post-screenshot animation
+        startAnimation();
+    }
+
+
+    /**
+     * Starts the animation after taking the screenshot
+     */
+    private void startAnimation() {
+        // Add the view for the animation
+        mScreenshotView.setImageBitmap(mScreenBitmap);
+        mScreenshotLayout.requestFocus();
+
+        // Setup the animation with the screenshot just taken
+        if (mScreenshotAnimation != null) {
+            mScreenshotAnimation.end();
+        }
+
+        mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
+        ValueAnimator screenshotFadeInAnim = createScreenshotFadeInAnimation();
+        ValueAnimator screenshotFadeOutAnim = createScreenshotFadeOutAnimation();
+        mScreenshotAnimation = new AnimatorSet();
+        mScreenshotAnimation.play(screenshotFadeInAnim).before(screenshotFadeOutAnim);
+        mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                // Save the screenshot once we have a bit of time now
+                saveScreenshotInWorkerThread();
+
+                mWindowManager.removeView(mScreenshotLayout);
+            }
+        });
+        mScreenshotAnimation.start();
+    }
+    private ValueAnimator createScreenshotFadeInAnimation() {
+        ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
+        anim.setInterpolator(mCubicInterpolator);
+        anim.setDuration(SCREENSHOT_FADE_IN_DURATION);
+        anim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mBackgroundView.setVisibility(View.VISIBLE);
+                mScreenshotContainerView.setVisibility(View.VISIBLE);
+            }
+        });
+        anim.addUpdateListener(new AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                float t = ((Float) animation.getAnimatedValue()).floatValue();
+                mBackgroundView.setAlpha(mBackgroundViewAlphaInterpolator.getInterpolation(t) *
+                        BACKGROUND_ALPHA);
+                float scaleT = SCREENSHOT_SCALE + (1f - t) * SCREENSHOT_SCALE;
+                mScreenshotContainerView.setAlpha(t*t*t*t);
+                mScreenshotContainerView.setScaleX(scaleT);
+                mScreenshotContainerView.setScaleY(scaleT);
+                mScreenshotContainerView.setRotation(t * SCREENSHOT_ROTATION);
+            }
+        });
+        return anim;
+    }
+    private ValueAnimator createScreenshotFadeOutAnimation() {
+        ValueAnimator anim = ValueAnimator.ofFloat(1f, 0f);
+        anim.setInterpolator(mCubicInterpolator);
+        anim.setStartDelay(SCREENSHOT_FADE_OUT_DELAY);
+        anim.setDuration(SCREENSHOT_FADE_OUT_DURATION);
+        anim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mBackgroundView.setVisibility(View.GONE);
+                mScreenshotContainerView.setVisibility(View.GONE);
+            }
+        });
+        anim.addUpdateListener(new AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                float t = ((Float) animation.getAnimatedValue()).floatValue();
+                float scaleT = SCREENSHOT_MIN_SCALE +
+                        t*(SCREENSHOT_SCALE - SCREENSHOT_MIN_SCALE);
+                mScreenshotContainerView.setAlpha(t);
+                mScreenshotContainerView.setScaleX(scaleT);
+                mScreenshotContainerView.setScaleY(scaleT);
+                mBackgroundView.setAlpha(t * t * BACKGROUND_ALPHA);
+            }
+        });
+        return anim;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
new file mode 100644
index 0000000..35eaedf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -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.
+ */
+
+package com.android.systemui.screenshot;
+
+import android.app.Service;
+import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
+import android.hardware.usb.UsbAccessory;
+import android.hardware.usb.UsbManager;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+
+import com.android.systemui.R;
+
+public class TakeScreenshotService extends Service {
+    private static final String TAG = "TakeScreenshotService";
+
+    private static GlobalScreenshot mScreenshot;
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (mScreenshot == null) {
+            mScreenshot = new GlobalScreenshot(this);
+        }
+        mScreenshot.takeScreenshot();
+        return null;
+    }
+}
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index b52e7e1..ad6cebb 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -28,6 +28,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.ServiceConnection;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.res.CompatibilityInfo;
@@ -372,6 +373,10 @@
     // What we do when the user long presses on home
     private int mLongPressOnHomeBehavior = -1;
 
+    // Screenshot trigger states
+    private boolean mVolumeDownTriggered;
+    private boolean mPowerDownTriggered;
+
     ShortcutManager mShortcutManager;
     PowerManager.WakeLock mBroadcastWakeLock;
 
@@ -2339,6 +2344,26 @@
         }
     }
 
+    private void takeScreenshot() {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                ComponentName cn = new ComponentName("com.android.systemui",
+                        "com.android.systemui.screenshot.TakeScreenshotService");
+                Intent intent = new Intent();
+                intent.setComponent(cn);
+                ServiceConnection conn = new ServiceConnection() {
+                    @Override
+                    public void onServiceConnected(ComponentName name, IBinder service) {}
+                    @Override
+                    public void onServiceDisconnected(ComponentName name) {}
+                };
+                mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
+                mContext.unbindService(conn);
+            }
+        });
+    }
+
     /** {@inheritDoc} */
     @Override
     public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) {
@@ -2398,6 +2423,24 @@
         // Handle special keys.
         switch (keyCode) {
             case KeyEvent.KEYCODE_VOLUME_DOWN:
+                if (down) {
+                    // If the power key down was already triggered, take the screenshot
+                    if (mPowerDownTriggered) {
+                        // Dismiss the power-key longpress
+                        mHandler.removeCallbacks(mPowerLongPress);
+                        mPowerKeyHandled = true;
+
+                        // Take the screenshot
+                        takeScreenshot();
+
+                        // Prevent the event from being passed through to the current activity
+                        result &= ~ACTION_PASS_TO_USER;
+                        break;
+                    }
+                    mVolumeDownTriggered = true;
+                } else {
+                    mVolumeDownTriggered = false;
+                }
             case KeyEvent.KEYCODE_VOLUME_UP:
             case KeyEvent.KEYCODE_VOLUME_MUTE: {
                 if (down) {
@@ -2478,6 +2521,18 @@
             case KeyEvent.KEYCODE_POWER: {
                 result &= ~ACTION_PASS_TO_USER;
                 if (down) {
+                    // If the volume down key has been triggered, then just take the screenshot
+                    if (mVolumeDownTriggered) {
+                        // Take the screenshot
+                        takeScreenshot();
+                        mPowerKeyHandled = true;
+
+                        // Prevent the event from being passed through to the current activity
+                        break;
+                    }
+                    mPowerDownTriggered = true;
+
+
                     ITelephony telephonyService = getTelephonyService();
                     boolean hungUp = false;
                     if (telephonyService != null) {
@@ -2499,6 +2554,7 @@
                     }
                     interceptPowerKeyDown(!isScreenOn || hungUp);
                 } else {
+                    mPowerDownTriggered = false;
                     if (interceptPowerKeyUp(canceled)) {
                         result = (result & ~ACTION_POKE_USER_ACTIVITY) | ACTION_GO_TO_SLEEP;
                     }
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 22e2dde..ea5d26b 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -157,7 +157,6 @@
     private static final int RADIO_UID = Process.PHONE_UID;
     private static final int LOG_UID = Process.LOG_UID;
     private static final int NFC_UID = Process.NFC_UID;
-    private static final int KEYCHAIN_UID = Process.KEYCHAIN_UID;
     static final int FIRST_APPLICATION_UID =
         Process.FIRST_APPLICATION_UID;
     static final int MAX_APPLICATION_UIDS = 1000;
@@ -761,10 +760,6 @@
                 MULTIPLE_APPLICATION_UIDS
                         ? NFC_UID : FIRST_APPLICATION_UID,
                 ApplicationInfo.FLAG_SYSTEM);
-        mSettings.addSharedUserLPw("android.uid.keychain",
-                MULTIPLE_APPLICATION_UIDS
-                        ? KEYCHAIN_UID : FIRST_APPLICATION_UID,
-                ApplicationInfo.FLAG_SYSTEM);
 
         String separateProcesses = SystemProperties.get("debug.separate_processes");
         if (separateProcesses != null && separateProcesses.length() > 0) {
diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java
index 918f1b6..13a76b3 100644
--- a/services/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/java/com/android/server/usb/UsbDeviceManager.java
@@ -45,7 +45,6 @@
 import android.os.SystemProperties;
 import android.os.UEventObserver;
 import android.provider.Settings;
-import android.util.Log;
 import android.util.Slog;
 
 import java.io.File;
@@ -62,7 +61,7 @@
 public class UsbDeviceManager {
 
     private static final String TAG = UsbDeviceManager.class.getSimpleName();
-    private static final boolean LOG = false;
+    private static final boolean DEBUG = false;
 
     private static final String USB_STATE_MATCH =
             "DEVPATH=/devices/virtual/android_usb/android0";
@@ -93,18 +92,9 @@
     private final UsbSettingsManager mSettingsManager;
     private NotificationManager mNotificationManager;
     private final boolean mHasUsbAccessory;
-
-    // for USB connected notification
-    private boolean mUsbNotificationShown;
     private boolean mUseUsbNotification;
-    private Notification mUsbNotification;
-
-    // for adb connected notification
-    private boolean mAdbNotificationShown;
-    private Notification mAdbNotification;
     private boolean mAdbEnabled;
 
-
     private class AdbSettingsObserver extends ContentObserver {
         public AdbSettingsObserver() {
             super(null);
@@ -117,115 +107,20 @@
         }
     }
 
-    private void updateUsbNotification(boolean connected) {
-        if (mNotificationManager == null || !mUseUsbNotification) return;
-        if (connected) {
-            if (!mUsbNotificationShown) {
-                Resources r = mContext.getResources();
-                CharSequence title = r.getText(
-                        com.android.internal.R.string.usb_preferences_notification_title);
-                CharSequence message = r.getText(
-                        com.android.internal.R.string.usb_preferece_notification_message);
-
-                if (mUsbNotification == null) {
-                    mUsbNotification = new Notification();
-                    mUsbNotification.icon = com.android.internal.R.drawable.stat_sys_data_usb;
-                    mUsbNotification.when = 0;
-                    mUsbNotification.flags = Notification.FLAG_ONGOING_EVENT;
-                    mUsbNotification.tickerText = title;
-                    mUsbNotification.defaults = 0; // please be quiet
-                    mUsbNotification.sound = null;
-                    mUsbNotification.vibrate = null;
-                }
-
-                Intent intent = new Intent();
-                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
-                        Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-                intent.setClassName("com.android.systemui",
-                        "com.android.systemui.usb.UsbPreferenceActivity");
-                PendingIntent pi = PendingIntent.getActivity(mContext, 0,
-                        intent, 0);
-
-                mUsbNotification.setLatestEventInfo(mContext, title, message, pi);
-
-                mUsbNotificationShown = true;
-                mNotificationManager.notify(
-                        com.android.internal.R.string.usb_preferences_notification_title,
-                        mUsbNotification);
-            }
-
-        } else if (mUsbNotificationShown) {
-            mUsbNotificationShown = false;
-            mNotificationManager.cancel(
-                    com.android.internal.R.string.usb_preferences_notification_title);
-        }
-    }
-
-    private void updateAdbNotification(boolean adbEnabled) {
-        if (mNotificationManager == null) return;
-        if (adbEnabled) {
-            if ("0".equals(SystemProperties.get("persist.adb.notify"))) return;
-
-            if (!mAdbNotificationShown) {
-                Resources r = mContext.getResources();
-                CharSequence title = r.getText(
-                        com.android.internal.R.string.adb_active_notification_title);
-                CharSequence message = r.getText(
-                        com.android.internal.R.string.adb_active_notification_message);
-
-                if (mAdbNotification == null) {
-                    mAdbNotification = new Notification();
-                    mAdbNotification.icon = com.android.internal.R.drawable.stat_sys_adb;
-                    mAdbNotification.when = 0;
-                    mAdbNotification.flags = Notification.FLAG_ONGOING_EVENT;
-                    mAdbNotification.tickerText = title;
-                    mAdbNotification.defaults = 0; // please be quiet
-                    mAdbNotification.sound = null;
-                    mAdbNotification.vibrate = null;
-                }
-
-                Intent intent = new Intent(
-                        Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
-                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
-                        Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-                // Note: we are hard-coding the component because this is
-                // an important security UI that we don't want anyone
-                // intercepting.
-                intent.setComponent(new ComponentName("com.android.settings",
-                        "com.android.settings.DevelopmentSettings"));
-                PendingIntent pi = PendingIntent.getActivity(mContext, 0,
-                        intent, 0);
-
-                mAdbNotification.setLatestEventInfo(mContext, title, message, pi);
-
-                mAdbNotificationShown = true;
-                mNotificationManager.notify(
-                        com.android.internal.R.string.adb_active_notification_title,
-                        mAdbNotification);
-            }
-        } else if (mAdbNotificationShown) {
-            mAdbNotificationShown = false;
-            mNotificationManager.cancel(
-                    com.android.internal.R.string.adb_active_notification_title);
-        }
-    }
-
     /*
      * Listens for uevent messages from the kernel to monitor the USB state
      */
     private final UEventObserver mUEventObserver = new UEventObserver() {
         @Override
         public void onUEvent(UEventObserver.UEvent event) {
-            if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                Slog.v(TAG, "USB UEVENT: " + event.toString());
-            }
+            if (DEBUG) Slog.v(TAG, "USB UEVENT: " + event.toString());
 
             String state = event.get("USB_STATE");
             String accessory = event.get("ACCESSORY");
             if (state != null) {
                 mHandler.updateState(state);
             } else if ("START".equals(accessory)) {
-                Slog.d(TAG, "got accessory start");
+                if (DEBUG) Slog.d(TAG, "got accessory start");
                 setCurrentFunction(UsbManager.USB_FUNCTION_ACCESSORY, false);
             }
         }
@@ -319,16 +214,32 @@
         private String mDefaultFunctions;
         private UsbAccessory mCurrentAccessory;
         private boolean mDeferAccessoryAttached;
+        private int mUsbNotificationId;
+        private boolean mAdbNotificationShown;
+
+        private static final int NOTIFICATION_NONE = 0;
+        private static final int NOTIFICATION_MTP = 1;
+        private static final int NOTIFICATION_PTP = 2;
+        private static final int NOTIFICATION_INSTALLER = 3;
+        private static final int NOTIFICATION_ADB = 4;
 
         public UsbHandler() {
-            // Read initial USB state
             try {
+                // sanity check the sys.usb.config system property
+                // this may be necessary if we crashed while switching USB configurations
+                String config = SystemProperties.get("sys.usb.config", "none");
+                if (config.equals("none")) {
+                    String persistConfig = SystemProperties.get("persist.sys.usb.config", "none");
+                    Slog.w(TAG, "resetting config to persistent property: " + persistConfig);
+                    SystemProperties.set("sys.usb.config", persistConfig);
+                }
+
+                // Read initial USB state
                 mCurrentFunctions = FileUtils.readTextFile(
                         new File(FUNCTIONS_PATH), 0, null).trim();
                 mDefaultFunctions = mCurrentFunctions;
                 String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim();
                 updateState(state);
-
                 mAdbEnabled = containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ADB);
 
                 // Upgrade step for previous versions that used persist.service.adb.enable
@@ -414,12 +325,12 @@
                 } catch (InterruptedException e) {
                 }
             }
-            Log.e(TAG, "waitForState(" + state + ") FAILED");
+            Slog.e(TAG, "waitForState(" + state + ") FAILED");
             return false;
         }
 
         private boolean setUsbConfig(String config) {
-            Log.d(TAG, "setUsbConfig(" + config + ")");
+            if (DEBUG) Slog.d(TAG, "setUsbConfig(" + config + ")");
             // set the new configuration
             SystemProperties.set("sys.usb.config", config);
             return waitForState(config);
@@ -428,7 +339,7 @@
         private void doSetCurrentFunctions(String functions) {
             if (!mCurrentFunctions.equals(functions)) {
                 if (!setUsbConfig("none") || !setUsbConfig(functions)) {
-                    Log.e(TAG, "Failed to switch USB configuration to " + functions);
+                    Slog.e(TAG, "Failed to switch USB configuration to " + functions);
                     // revert to previous configuration if we fail
                     setUsbConfig(mCurrentFunctions);
                 } else {
@@ -438,6 +349,7 @@
         }
 
         private void setAdbEnabled(boolean enable) {
+            if (DEBUG) Slog.d(TAG, "setAdbEnabled: " + enable);
             if (enable != mAdbEnabled) {
                 mAdbEnabled = enable;
                 String functions;
@@ -449,7 +361,7 @@
                     functions = removeFunction(mDefaultFunctions, UsbManager.USB_FUNCTION_ADB);
                 }
                 setCurrentFunction(functions, true);
-                updateAdbNotification(mAdbEnabled && mConnected);
+                updateAdbNotification();
             }
         }
 
@@ -469,7 +381,7 @@
                 String[] strings = nativeGetAccessoryStrings();
                 if (strings != null) {
                     mCurrentAccessory = new UsbAccessory(strings);
-                    Log.d(TAG, "entering USB accessory mode: " + mCurrentAccessory);
+                    Slog.d(TAG, "entering USB accessory mode: " + mCurrentAccessory);
                     // defer accessoryAttached if system is not ready
                     if (mSystemReady) {
                         mSettingsManager.accessoryAttached(mCurrentAccessory);
@@ -477,12 +389,12 @@
                         mDeferAccessoryAttached = true;
                     }
                 } else {
-                    Log.e(TAG, "nativeGetAccessoryStrings failed");
+                    Slog.e(TAG, "nativeGetAccessoryStrings failed");
                 }
             } else if (!mConnected) {
                 // make sure accessory mode is off
                 // and restore default functions
-                Log.d(TAG, "exited USB accessory mode");
+                Slog.d(TAG, "exited USB accessory mode");
                 setEnabledFunctions(mDefaultFunctions);
 
                 if (mCurrentAccessory != null) {
@@ -517,8 +429,8 @@
                 case MSG_UPDATE_STATE:
                     mConnected = (msg.arg1 == 1);
                     mConfigured = (msg.arg2 == 1);
-                    updateUsbNotification(mConnected);
-                    updateAdbNotification(mAdbEnabled && mConnected);
+                    updateUsbNotification();
+                    updateAdbNotification();
                     if (containsFunction(mCurrentFunctions,
                             UsbManager.USB_FUNCTION_ACCESSORY)) {
                         updateCurrentAccessory();
@@ -562,8 +474,8 @@
                     }
                     break;
                 case MSG_SYSTEM_READY:
-                    updateUsbNotification(mConnected);
-                    updateAdbNotification(mAdbEnabled && mConnected);
+                    updateUsbNotification();
+                    updateAdbNotification();
                     updateUsbState();
                     if (mCurrentAccessory != null && mDeferAccessoryAttached) {
                         mSettingsManager.accessoryAttached(mCurrentAccessory);
@@ -576,6 +488,105 @@
             return mCurrentAccessory;
         }
 
+        private void updateUsbNotification() {
+            if (mNotificationManager == null || !mUseUsbNotification) return;
+            if (mConnected) {
+                Resources r = mContext.getResources();
+                CharSequence title = null;
+                int id = NOTIFICATION_NONE;
+                if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MTP)) {
+                    title = r.getText(
+                        com.android.internal.R.string.usb_mtp_notification_title);
+                    id = NOTIFICATION_MTP;
+                } else if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_PTP)) {
+                    title = r.getText(
+                        com.android.internal.R.string.usb_ptp_notification_title);
+                    id = NOTIFICATION_PTP;
+                } else if (containsFunction(mCurrentFunctions,
+                        UsbManager.USB_FUNCTION_MASS_STORAGE)) {
+                    title = r.getText(
+                        com.android.internal.R.string.usb_cd_installer_notification_title);
+                    id = NOTIFICATION_INSTALLER;
+                } else {
+                    Slog.e(TAG, "No known USB function in updateUsbNotification");
+                }
+                if (id != mUsbNotificationId) {
+                    // clear notification if title needs changing
+                    if (mUsbNotificationId != NOTIFICATION_NONE) {
+                        mNotificationManager.cancel(mUsbNotificationId);
+                        mUsbNotificationId = NOTIFICATION_NONE;
+                    }
+                }
+                if (mUsbNotificationId == NOTIFICATION_NONE) {
+                    CharSequence message = r.getText(
+                            com.android.internal.R.string.usb_notification_message);
+
+                    Notification notification = new Notification();
+                    notification.icon = com.android.internal.R.drawable.stat_sys_data_usb;
+                    notification.when = 0;
+                    notification.flags = Notification.FLAG_ONGOING_EVENT;
+                    notification.tickerText = title;
+                    notification.defaults = 0; // please be quiet
+                    notification.sound = null;
+                    notification.vibrate = null;
+
+                    Intent intent = new Intent();
+                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+                            Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+                    intent.setClassName("com.android.systemui",
+                            "com.android.systemui.usb.UsbPreferenceActivity");
+                    PendingIntent pi = PendingIntent.getActivity(mContext, 0,
+                            intent, 0);
+                    notification.setLatestEventInfo(mContext, title, message, pi);
+                    mNotificationManager.notify(id, notification);
+                    mUsbNotificationId = id;
+                }
+
+            } else if (mUsbNotificationId != NOTIFICATION_NONE) {
+                mNotificationManager.cancel(mUsbNotificationId);
+                mUsbNotificationId = NOTIFICATION_NONE;
+            }
+        }
+
+        private void updateAdbNotification() {
+            if (mNotificationManager == null) return;
+            if (mAdbEnabled && mConnected) {
+                if ("0".equals(SystemProperties.get("persist.adb.notify"))) return;
+
+                if (!mAdbNotificationShown) {
+                    Resources r = mContext.getResources();
+                    CharSequence title = r.getText(
+                            com.android.internal.R.string.adb_active_notification_title);
+                    CharSequence message = r.getText(
+                            com.android.internal.R.string.adb_active_notification_message);
+
+                    Notification notification = new Notification();
+                    notification.icon = com.android.internal.R.drawable.stat_sys_adb;
+                    notification.when = 0;
+                    notification.flags = Notification.FLAG_ONGOING_EVENT;
+                    notification.tickerText = title;
+                    notification.defaults = 0; // please be quiet
+                    notification.sound = null;
+                    notification.vibrate = null;
+
+                    Intent intent = new Intent(
+                            Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
+                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+                            Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+                    intent.setComponent(new ComponentName("com.android.settings",
+                            "com.android.settings.DevelopmentSettings"));
+                    PendingIntent pi = PendingIntent.getActivity(mContext, 0,
+                            intent, 0);
+                    notification.setLatestEventInfo(mContext, title, message, pi);
+                    mAdbNotificationShown = true;
+                    mNotificationManager.notify(NOTIFICATION_ADB, notification);
+                }
+            } else if (mAdbNotificationShown) {
+                mAdbNotificationShown = false;
+                mNotificationManager.cancel(NOTIFICATION_ADB);
+            }
+        }
+
         public void dump(FileDescriptor fd, PrintWriter pw) {
             pw.println("  USB Device State:");
             pw.println("    Current Functions: " + mCurrentFunctions);
@@ -608,6 +619,7 @@
         }
 
     public void setCurrentFunction(String function, boolean makeDefault) {
+        if (DEBUG) Slog.d(TAG, "setCurrentFunction(" + function + ") default: " + makeDefault);
         mHandler.sendMessage(MSG_SET_CURRENT_FUNCTION, function, makeDefault);
     }
 
diff --git a/services/java/com/android/server/usb/UsbHostManager.java b/services/java/com/android/server/usb/UsbHostManager.java
index 923b049..0a0ff59 100644
--- a/services/java/com/android/server/usb/UsbHostManager.java
+++ b/services/java/com/android/server/usb/UsbHostManager.java
@@ -37,7 +37,6 @@
 import android.os.ParcelFileDescriptor;
 import android.os.UEventObserver;
 import android.provider.Settings;
-import android.util.Log;
 import android.util.Slog;
 
 import java.io.File;
@@ -112,7 +111,7 @@
 
         synchronized (mLock) {
             if (mDevices.get(deviceName) != null) {
-                Log.w(TAG, "device already on mDevices list: " + deviceName);
+                Slog.w(TAG, "device already on mDevices list: " + deviceName);
                 return;
             }
 
@@ -148,7 +147,7 @@
             } catch (Exception e) {
                 // beware of index out of bound exceptions, which might happen if
                 // a device does not set bNumEndpoints correctly
-                Log.e(TAG, "error parsing USB descriptors", e);
+                Slog.e(TAG, "error parsing USB descriptors", e);
                 return;
             }
 
diff --git a/services/java/com/android/server/usb/UsbSettingsManager.java b/services/java/com/android/server/usb/UsbSettingsManager.java
index 9113677..0baafbb 100644
--- a/services/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/java/com/android/server/usb/UsbSettingsManager.java
@@ -35,7 +35,7 @@
 import android.os.Binder;
 import android.os.FileUtils;
 import android.os.Process;
-import android.util.Log;
+import android.util.Slog;
 import android.util.SparseBooleanArray;
 import android.util.Xml;
 
@@ -62,6 +62,7 @@
 class UsbSettingsManager {
 
     private static final String TAG = "UsbSettingsManager";
+    private static final boolean DEBUG = false;
     private static final File sSettingsFile = new File("/data/system/usb_device_manager.xml");
 
     private final Context mContext;
@@ -410,9 +411,9 @@
                 }
             }
         } catch (FileNotFoundException e) {
-            Log.w(TAG, "settings file not found");
+            if (DEBUG) Slog.d(TAG, "settings file not found");
         } catch (Exception e) {
-            Log.e(TAG, "error reading settings file, deleting to start fresh", e);
+            Slog.e(TAG, "error reading settings file, deleting to start fresh", e);
             sSettingsFile.delete();
         } finally {
             if (stream != null) {
@@ -428,7 +429,7 @@
         FileOutputStream fos = null;
         try {
             FileOutputStream fstr = new FileOutputStream(sSettingsFile);
-            Log.d(TAG, "writing settings to " + fstr);
+            if (DEBUG) Slog.d(TAG, "writing settings to " + fstr);
             BufferedOutputStream str = new BufferedOutputStream(fstr);
             FastXmlSerializer serializer = new FastXmlSerializer();
             serializer.setOutput(str, "utf-8");
@@ -457,7 +458,7 @@
             FileUtils.sync(fstr);
             str.close();
         } catch (Exception e) {
-            Log.e(TAG, "error writing settings file, deleting to start fresh", e);
+            Slog.e(TAG, "error writing settings file, deleting to start fresh", e);
             sSettingsFile.delete();
         }
     }
@@ -472,7 +473,7 @@
         try {
             parser = ai.loadXmlMetaData(mPackageManager, metaDataName);
             if (parser == null) {
-                Log.w(TAG, "no meta-data for " + info);
+                Slog.w(TAG, "no meta-data for " + info);
                 return false;
             }
 
@@ -494,7 +495,7 @@
                 XmlUtils.nextElement(parser);
             }
         } catch (Exception e) {
-            Log.w(TAG, "Unable to load component info " + info.toString(), e);
+            Slog.w(TAG, "Unable to load component info " + info.toString(), e);
         } finally {
             if (parser != null) parser.close();
         }
@@ -553,7 +554,7 @@
 
         Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED);
         intent.putExtra(UsbManager.EXTRA_DEVICE, device);
-        Log.d(TAG, "usbDeviceRemoved, sending " + intent);
+        if (DEBUG) Slog.d(TAG, "usbDeviceRemoved, sending " + intent);
         mContext.sendBroadcast(intent);
     }
 
@@ -604,7 +605,7 @@
                     try {
                         mContext.startActivity(dialogIntent);
                     } catch (ActivityNotFoundException e) {
-                        Log.e(TAG, "unable to start UsbAccessoryUriActivity");
+                        Slog.e(TAG, "unable to start UsbAccessoryUriActivity");
                     }
                 }
             }
@@ -652,7 +653,7 @@
                                 defaultRI.activityInfo.name));
                 mContext.startActivity(intent);
             } catch (ActivityNotFoundException e) {
-                Log.e(TAG, "startActivity failed", e);
+                Slog.e(TAG, "startActivity failed", e);
             }
         } else {
             Intent resolverIntent = new Intent();
@@ -679,7 +680,7 @@
             try {
                 mContext.startActivity(resolverIntent);
             } catch (ActivityNotFoundException e) {
-                Log.e(TAG, "unable to start activity " + resolverIntent);
+                Slog.e(TAG, "unable to start activity " + resolverIntent);
             }
         }
     }
@@ -733,7 +734,7 @@
                 XmlUtils.nextElement(parser);
             }
         } catch (Exception e) {
-            Log.w(TAG, "Unable to load component info " + aInfo.toString(), e);
+            Slog.w(TAG, "Unable to load component info " + aInfo.toString(), e);
         } finally {
             if (parser != null) parser.close();
         }
@@ -751,7 +752,7 @@
                 info = mPackageManager.getPackageInfo(packageName,
                         PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA);
             } catch (NameNotFoundException e) {
-                Log.e(TAG, "handlePackageUpdate could not find package " + packageName, e);
+                Slog.e(TAG, "handlePackageUpdate could not find package " + packageName, e);
                 return;
             }
 
@@ -831,7 +832,7 @@
         try {
             mContext.startActivity(intent);
         } catch (ActivityNotFoundException e) {
-            Log.e(TAG, "unable to start UsbPermissionActivity");
+            Slog.e(TAG, "unable to start UsbPermissionActivity");
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -847,7 +848,7 @@
             try {
                 pi.send(mContext, 0, intent);
             } catch (PendingIntent.CanceledException e) {
-                Log.w(TAG, "requestPermission PendingIntent was cancelled");
+                if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
             }
             return;
         }
@@ -867,7 +868,7 @@
            try {
                 pi.send(mContext, 0, intent);
             } catch (PendingIntent.CanceledException e) {
-                Log.w(TAG, "requestPermission PendingIntent was cancelled");
+                if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
             }
             return;
         }
diff --git a/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java b/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java
index dea67f3..ffabb7b 100644
--- a/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java
+++ b/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java
@@ -94,9 +94,6 @@
      */
     public PhoneNumberFormattingTextWatcher(String countryCode) {
         if (countryCode == null) throw new IllegalArgumentException();
-        // TODO: remove this once CountryDetector.detectCountry().getCountryIso() is fixed to always
-        // return uppercase. Tracked at b/4941319.
-        countryCode = countryCode.toUpperCase(Locale.ENGLISH);
         mFormatter = PhoneNumberUtil.getInstance().getAsYouTypeFormatter(countryCode);
     }
 
diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java
index ab93e2a..68e0045 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfo.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfo.java
@@ -526,16 +526,6 @@
                   + countryIso);
         }
 
-        // Temp workaround: The current libphonenumber library requires
-        // the countryIso to be uppercase (e.g. "US") but the
-        // detector.detectCountry().getCountryIso() call currently returns
-        // "us".  Passing "us" to util.parse() will just result in a
-        // NumberParseException.
-        // So force the countryIso to uppercase for now.
-        // TODO: remove this once getCountryIso() is fixed to always
-        // return uppercase.
-        countryIso = countryIso.toUpperCase();
-
         PhoneNumber pn = null;
         try {
             if (VDBG) Log.v(TAG, "parsing '" + number
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 67f5158..a6b131a 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -788,13 +788,13 @@
                         Message msg = obtainMessage(EVENT_DISCONNECT_DONE, apnContext);
                         apnContext.getDataConnection().tearDown(apnContext.getReason(), msg);
                         apnContext.setState(State.DISCONNECTING);
-                    } else {
-                        // apn is connected but no reference to dcac.
-                        // Should not be happen, but reset the state in case.
-                        apnContext.setState(State.IDLE);
-                        mPhone.notifyDataConnection(apnContext.getReason(),
-                                                    apnContext.getApnType());
                     }
+                } else {
+                    // apn is connected but no reference to dcac.
+                    // Should not be happen, but reset the state in case.
+                    apnContext.setState(State.IDLE);
+                    mPhone.notifyDataConnection(apnContext.getReason(),
+                                                apnContext.getApnType());
                 }
             }
         } else {
diff --git a/tests/GridLayoutTest/res/layout/grid3.xml b/tests/GridLayoutTest/res/layout/grid3.xml
index ba911c2..536be7e 100644
--- a/tests/GridLayoutTest/res/layout/grid3.xml
+++ b/tests/GridLayoutTest/res/layout/grid3.xml
@@ -66,8 +66,8 @@
     <Space
             android:layout_row="4"
             android:layout_column="2"
-            android:layout_rowWeight="1"
-            android:layout_columnWeight="1"
+            android:layout_heightSpec="canStretch"
+            android:layout_widthSpec="canStretch"
             />
 
     <Button
diff --git a/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java b/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java
index e010a00..cba98c2 100644
--- a/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java
+++ b/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java
@@ -97,8 +97,8 @@
             Space v = new Space(context);
             {
                 LayoutParams lp = new LayoutParams(row5, col3);
-                lp.rowWeight = 1;
-                lp.columnWeight = 1;
+                lp.widthSpec = CAN_STRETCH;
+                lp.heightSpec = CAN_STRETCH;
                 vg.addView(v, lp);
             }
         }
diff --git a/tests/RenderScriptTests/PerfTest/AndroidManifest.xml b/tests/RenderScriptTests/PerfTest/AndroidManifest.xml
index aafd176..cc60396 100644
--- a/tests/RenderScriptTests/PerfTest/AndroidManifest.xml
+++ b/tests/RenderScriptTests/PerfTest/AndroidManifest.xml
@@ -9,8 +9,7 @@
       android:icon="@drawable/test_pattern">
         <uses-library android:name="android.test.runner" />
         <activity android:name="RsBench"
-                  android:label="RsBenchmark"
-                  android:theme="@android:style/Theme.Black.NoTitleBar">
+                  android:label="RsBenchmark">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
diff --git a/tests/RenderScriptTests/PerfTest/res/menu/loader_menu.xml b/tests/RenderScriptTests/PerfTest/res/menu/loader_menu.xml
new file mode 100644
index 0000000..8234677
--- /dev/null
+++ b/tests/RenderScriptTests/PerfTest/res/menu/loader_menu.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* 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.
+*/
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/benchmark_mode"
+          android:title="@string/benchmark_mode" />
+    <item android:id="@+id/debug_mode"
+          android:title="@string/debug_mode" />
+</menu>
diff --git a/tests/RenderScriptTests/PerfTest/res/values/strings.xml b/tests/RenderScriptTests/PerfTest/res/values/strings.xml
new file mode 100644
index 0000000..627ac21
--- /dev/null
+++ b/tests/RenderScriptTests/PerfTest/res/values/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <skip />
+    <string name="benchmark_mode">Benchmark Mode</string>
+    <string name="debug_mode">Debug Mode</string>
+</resources>
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBench.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBench.java
index d7393f8..b336a4d 100644
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBench.java
+++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBench.java
@@ -31,10 +31,14 @@
 import android.util.Log;
 import android.view.Menu;
 import android.view.MenuItem;
+import android.view.MenuInflater;
 import android.view.View;
 import android.view.Window;
 import android.widget.Button;
 import android.widget.ListView;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.widget.Toast;
 
 import java.lang.Runtime;
 
@@ -77,4 +81,37 @@
         super.onPause();
         mView.pause();
     }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.loader_menu, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        // Handle item selection
+        switch (item.getItemId()) {
+            case R.id.benchmark_mode:
+                mView.setBenchmarkMode();
+                return true;
+            case R.id.debug_mode:
+                AlertDialog.Builder builder = new AlertDialog.Builder(this);
+                builder.setTitle("Pick a Test");
+                builder.setItems(mView.getTestNames(),
+                                 new DialogInterface.OnClickListener() {
+                    public void onClick(DialogInterface dialog, int item) {
+                        Toast.makeText(getApplicationContext(),
+                                       "Switching to: " + mView.getTestNames()[item],
+                                       Toast.LENGTH_SHORT).show();
+                        mView.setDebugMode(item);
+                    }
+                });
+                builder.show();
+                return true;
+            default:
+                return super.onOptionsItemSelected(item);
+        }
+    }
 }
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java
index c706286..c375be5 100644
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java
+++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java
@@ -84,12 +84,6 @@
     private Resources mRes;
     private RenderScriptGL mRS;
 
-    private Sampler mLinearClamp;
-    private Sampler mLinearWrap;
-    private Sampler mMipLinearWrap;
-    private Sampler mNearestClamp;
-    private Sampler mNearesWrap;
-
     private ProgramStore mProgStoreBlendNoneDepth;
     private ProgramStore mProgStoreBlendNone;
     private ProgramStore mProgStoreBlendAlpha;
@@ -115,10 +109,6 @@
     private ScriptField_FragentShaderConstants3_s mFSConstPixel;
 
 
-    private ProgramRaster mCullBack;
-    private ProgramRaster mCullFront;
-    private ProgramRaster mCullNone;
-
     private Allocation mTexTorus;
     private Allocation mTexOpaque;
     private Allocation mTexTransparent;
@@ -143,6 +133,8 @@
 
 
     private ScriptC_rsbench mScript;
+    private ScriptC_text_test mTextScript;
+    private ScriptC_torus_test mTorusScript;
 
     private final BitmapFactory.Options mOptionsARGB = new BitmapFactory.Options();
 
@@ -310,6 +302,7 @@
         mProgStoreBlendAdd = BLEND_ADD_DEPTH_NONE(mRS);
 
         mScript.set_gProgStoreBlendNoneDepth(mProgStoreBlendNoneDepth);
+
         mScript.set_gProgStoreBlendNone(mProgStoreBlendNone);
         mScript.set_gProgStoreBlendAlpha(mProgStoreBlendAlpha);
         mScript.set_gProgStoreBlendAdd(mProgStoreBlendAdd);
@@ -330,22 +323,24 @@
         texBuilder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE,
                               ProgramFragmentFixedFunction.Builder.Format.RGBA, 0);
         mProgFragmentTexture = texBuilder.create();
-        mProgFragmentTexture.bindSampler(mLinearClamp, 0);
+        mProgFragmentTexture.bindSampler(Sampler.CLAMP_LINEAR(mRS), 0);
 
         ProgramFragmentFixedFunction.Builder colBuilder = new ProgramFragmentFixedFunction.Builder(mRS);
         colBuilder.setVaryingColor(false);
         mProgFragmentColor = colBuilder.create();
 
         mScript.set_gProgFragmentColor(mProgFragmentColor);
+
         mScript.set_gProgFragmentTexture(mProgFragmentTexture);
 
 
+
         // For Galaxy live wallpaper drawing
         ProgramFragmentFixedFunction.Builder builder = new ProgramFragmentFixedFunction.Builder(mRS);
         builder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE,
                            ProgramFragmentFixedFunction.Builder.Format.RGB, 0);
         ProgramFragment pfb = builder.create();
-        pfb.bindSampler(mNearesWrap, 0);
+        pfb.bindSampler(Sampler.WRAP_NEAREST(mRS), 0);
         mScript.set_gPFBackground(pfb);
 
         builder = new ProgramFragmentFixedFunction.Builder(mRS);
@@ -354,7 +349,7 @@
                            ProgramFragmentFixedFunction.Builder.Format.RGBA, 0);
         builder.setVaryingColor(true);
         ProgramFragment pfs = builder.create();
-        pfs.bindSampler(mMipLinearWrap, 0);
+        pfs.bindSampler(Sampler.WRAP_LINEAR_MIP_LINEAR(mRS), 0);
         mScript.set_gPFStars(pfs);
 
     }
@@ -404,6 +399,7 @@
 
         mScript.set_gProgVertex(mProgVertex);
 
+
         // For galaxy live wallpaper
         mPvStarAlloc = new ScriptField_VpConsts(mRS, 1);
         mScript.bind_vpConstants(mPvStarAlloc);
@@ -447,13 +443,11 @@
     private void initCustomShaders() {
         mVSConst = new ScriptField_VertexShaderConstants_s(mRS, 1);
         mFSConst = new ScriptField_FragentShaderConstants_s(mRS, 1);
-        mScript.bind_gVSConstants(mVSConst);
-        mScript.bind_gFSConstants(mFSConst);
+
 
         mVSConstPixel = new ScriptField_VertexShaderConstants3_s(mRS, 1);
         mFSConstPixel = new ScriptField_FragentShaderConstants3_s(mRS, 1);
-        mScript.bind_gVSConstPixel(mVSConstPixel);
-        mScript.bind_gFSConstPixel(mFSConstPixel);
+
 
         // Initialize the shader builder
         ProgramVertex.Builder pvbCustom = new ProgramVertex.Builder(mRS);
@@ -506,11 +500,7 @@
         }
         mProgFragmentMultitex = pfbCustom.create();
 
-        mScript.set_gProgVertexCustom(mProgVertexCustom);
-        mScript.set_gProgFragmentCustom(mProgFragmentCustom);
-        mScript.set_gProgVertexPixelLight(mProgVertexPixelLight);
-        mScript.set_gProgVertexPixelLightMove(mProgVertexPixelLightMove);
-        mScript.set_gProgFragmentPixelLight(mProgFragmentPixelLight);
+
         mScript.set_gProgFragmentMultitex(mProgFragmentMultitex);
     }
 
@@ -587,39 +577,22 @@
             Log.e("rs", "could not load model");
         } else {
             mTorus = (Mesh)entry.getObject();
-            mScript.set_gTorusMesh(mTorus);
         }
 
         createParticlesMesh();
     }
 
     private void initSamplers() {
-        Sampler.Builder bs = new Sampler.Builder(mRS);
-        bs.setMinification(Sampler.Value.LINEAR);
-        bs.setMagnification(Sampler.Value.LINEAR);
-        bs.setWrapS(Sampler.Value.WRAP);
-        bs.setWrapT(Sampler.Value.WRAP);
-        mLinearWrap = bs.create();
-
-        mLinearClamp = Sampler.CLAMP_LINEAR(mRS);
-        mNearestClamp = Sampler.CLAMP_NEAREST(mRS);
-        mMipLinearWrap = Sampler.WRAP_LINEAR_MIP_LINEAR(mRS);
-        mNearesWrap = Sampler.WRAP_NEAREST(mRS);
-
-        mScript.set_gLinearClamp(mLinearClamp);
-        mScript.set_gLinearWrap(mLinearWrap);
-        mScript.set_gMipLinearWrap(mMipLinearWrap);
-        mScript.set_gNearestClamp(mNearestClamp);
+        mScript.set_gLinearClamp(Sampler.CLAMP_LINEAR(mRS));
+        mScript.set_gLinearWrap(Sampler.WRAP_LINEAR(mRS));
+        mScript.set_gMipLinearWrap(Sampler.WRAP_LINEAR_MIP_LINEAR(mRS));
+        mScript.set_gNearestClamp(Sampler.CLAMP_NEAREST(mRS));
     }
 
     private void initProgramRaster() {
-        mCullBack = ProgramRaster.CULL_BACK(mRS);
-        mCullFront = ProgramRaster.CULL_FRONT(mRS);
-        mCullNone = ProgramRaster.CULL_NONE(mRS);
-
-        mScript.set_gCullBack(mCullBack);
-        mScript.set_gCullFront(mCullFront);
-        mScript.set_gCullNone(mCullNone);
+        mScript.set_gCullBack(ProgramRaster.CULL_BACK(mRS));
+        mScript.set_gCullFront(ProgramRaster.CULL_FRONT(mRS));
+        mScript.set_gCullNone(ProgramRaster.CULL_NONE(mRS));
     }
 
     private int strlen(byte[] array) {
@@ -645,9 +618,47 @@
         }
     }
 
+    public void setDebugMode(int num) {
+        mScript.invoke_setDebugMode(num);
+    }
+
+    public void setBenchmarkMode() {
+        mScript.invoke_setBenchmarkMode();
+    }
+
+    void initTextScript() {
+        mTextScript = new ScriptC_text_test(mRS, mRes, R.raw.text_test);
+        mTextScript.set_gFontSans(mFontSans);
+        mTextScript.set_gFontSerif(mFontSerif);
+    }
+
+    void initTorusScript() {
+        mTorusScript = new ScriptC_torus_test(mRS, mRes, R.raw.torus_test);
+        mTorusScript.set_gCullFront(ProgramRaster.CULL_FRONT(mRS));
+        mTorusScript.set_gCullBack(ProgramRaster.CULL_BACK(mRS));
+        mTorusScript.set_gLinearClamp(Sampler.CLAMP_LINEAR(mRS));
+        mTorusScript.set_gTorusMesh(mTorus);
+        mTorusScript.set_gTexTorus(mTexTorus);
+        mTorusScript.set_gProgVertexCustom(mProgVertexCustom);
+        mTorusScript.set_gProgFragmentCustom(mProgFragmentCustom);
+        mTorusScript.set_gProgVertexPixelLight(mProgVertexPixelLight);
+        mTorusScript.set_gProgVertexPixelLightMove(mProgVertexPixelLightMove);
+        mTorusScript.set_gProgFragmentPixelLight(mProgFragmentPixelLight);
+        mTorusScript.bind_gVSConstPixel(mVSConstPixel);
+        mTorusScript.bind_gFSConstPixel(mFSConstPixel);
+        mTorusScript.bind_gVSConstants(mVSConst);
+        mTorusScript.bind_gFSConstants(mFSConst);
+        mTorusScript.set_gProgVertex(mProgVertex);
+        mTorusScript.set_gProgFragmentTexture(mProgFragmentTexture);
+        mTorusScript.set_gProgFragmentColor(mProgFragmentColor);
+        mTorusScript.set_gProgStoreBlendNoneDepth(mProgStoreBlendNoneDepth);
+    }
+
     private void initRS() {
 
         mScript = new ScriptC_rsbench(mRS, mRes, R.raw.rsbench);
+
+
         mRS.setMessageHandler(mRsMessage);
 
         mMaxModes = mScript.get_gMaxModes();
@@ -709,6 +720,13 @@
         mSampleListViewAllocs.copyAll();
         mScript.bind_gListViewText(mSampleListViewAllocs);
 
+        initTextScript();
+        initTorusScript();
+
+        mScript.set_gFontScript(mTextScript);
+        mScript.set_gTorusScript(mTorusScript);
+        mScript.set_gDummyAlloc(Allocation.createSized(mRS, Element.I32(mRS), 1));
+
         mRS.bindRootScript(mScript);
     }
 }
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchView.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchView.java
index 2882b93..61aa3e1 100644
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchView.java
+++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchView.java
@@ -105,4 +105,16 @@
     public boolean testIsFinished() {
         return mRender.testIsFinished();
     }
+
+    void setBenchmarkMode() {
+        mRender.setBenchmarkMode();
+    }
+
+    void setDebugMode(int num) {
+        mRender.setDebugMode(num);
+    }
+
+    String[] getTestNames() {
+        return mRender.mTestNames;
+    }
 }
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs
index bb81862..eaafe1d 100644
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs
+++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs
@@ -18,6 +18,7 @@
 
 #include "rs_graphics.rsh"
 #include "shader_def.rsh"
+#include "subtest_def.rsh"
 
 /* Message sent from script to renderscript */
 const int RS_MSG_TEST_DONE = 100;
@@ -98,7 +99,6 @@
 rs_mesh g10by10Mesh;
 rs_mesh g100by100Mesh;
 rs_mesh gWbyHMesh;
-rs_mesh gTorusMesh;
 rs_mesh gSingleMesh;
 
 rs_font gFontSans;
@@ -115,25 +115,15 @@
 rs_program_raster gCullFront;
 rs_program_raster gCullNone;
 
-// Custom vertex shader compunents
-VertexShaderConstants *gVSConstants;
-FragentShaderConstants *gFSConstants;
-VertexShaderConstants3 *gVSConstPixel;
-FragentShaderConstants3 *gFSConstPixel;
 // Export these out to easily set the inputs to shader
 VertexShaderInputs *gVSInputs;
-// Custom shaders we use for lighting
-rs_program_vertex gProgVertexCustom;
-rs_program_fragment gProgFragmentCustom;
-rs_program_vertex gProgVertexPixelLight;
-rs_program_vertex gProgVertexPixelLightMove;
-rs_program_fragment gProgFragmentPixelLight;
+
 rs_program_fragment gProgFragmentMultitex;
 
 rs_allocation gRenderBufferColor;
 rs_allocation gRenderBufferDepth;
 
-float gDt = 0;
+static float gDt = 0;
 
 void init() {
 }
@@ -141,16 +131,6 @@
 static int gRenderSurfaceW;
 static int gRenderSurfaceH;
 
-static const char *sampleText = "This is a sample of small text for performace";
-// Offsets for multiple layer of text
-static int textOffsets[] = { 0,  0, -5, -5, 5,  5, -8, -8, 8,  8};
-static float textColors[] = {1.0f, 1.0f, 1.0f, 1.0f,
-                             0.5f, 0.7f, 0.5f, 1.0f,
-                             0.7f, 0.5f, 0.5f, 1.0f,
-                             0.5f, 0.5f, 0.7f, 1.0f,
-                             0.5f, 0.6f, 0.7f, 1.0f,
-};
-
 /**
   * Methods to draw the galaxy live wall paper
   */
@@ -291,40 +271,16 @@
     rsgBindDepthTarget(gRenderBufferDepth);
 }
 
+rs_script gFontScript;
+rs_script gTorusScript;
+rs_allocation gDummyAlloc;
+
 static void displayFontSamples(int fillNum) {
-
-    rs_font fonts[5];
-    fonts[0] = gFontSans;
-    fonts[1] = gFontSerif;
-    fonts[2] = gFontSans;
-    fonts[3] = gFontSerif;
-    fonts[4] = gFontSans;
-
-    uint width = gRenderSurfaceW;
-    uint height = gRenderSurfaceH;
-    int left = 0, right = 0, top = 0, bottom = 0;
-    rsgMeasureText(sampleText, &left, &right, &top, &bottom);
-
-    int textHeight = top - bottom;
-    int textWidth = right - left;
-    int numVerticalLines = height / textHeight;
-    int yPos = top;
-
-    int xOffset = 0, yOffset = 0;
-    for(int fillI = 0; fillI < fillNum; fillI ++) {
-        rsgBindFont(fonts[fillI]);
-        xOffset = textOffsets[fillI * 2];
-        yOffset = textOffsets[fillI * 2 + 1];
-        float *colPtr = textColors + fillI * 4;
-        rsgFontColor(colPtr[0], colPtr[1], colPtr[2], colPtr[3]);
-        for (int h = 0; h < 4; h ++) {
-            yPos = top + yOffset;
-            for (int v = 0; v < numVerticalLines; v ++) {
-                rsgDrawText(sampleText, xOffset + textWidth * h, yPos);
-                yPos += textHeight;
-            }
-        }
-    }
+    TestData testData;
+    testData.renderSurfaceW = gRenderSurfaceW;
+    testData.renderSurfaceH = gRenderSurfaceH;
+    testData.user = fillNum;
+    rsForEach(gFontScript, gDummyAlloc, gDummyAlloc, &testData);
 }
 
 static void bindProgramVertexOrtho() {
@@ -555,212 +511,37 @@
     drawMeshInPage(2.0f*gRenderSurfaceW, 0, wResolution, hResolution);
 }
 
-static float gTorusRotation = 0;
-static void updateModelMatrix(rs_matrix4x4 *matrix, void *buffer) {
-    if (buffer == 0) {
-        rsgProgramVertexLoadModelMatrix(matrix);
-    } else {
-        rsgAllocationSyncAll(rsGetAllocation(buffer));
-    }
-}
-
-static void drawToruses(int numMeshes, rs_matrix4x4 *matrix, void *buffer) {
-
-    if (numMeshes == 1) {
-        rsMatrixLoadTranslate(matrix, 0.0f, 0.0f, -7.5f);
-        rsMatrixRotate(matrix, gTorusRotation, 1.0f, 0.0f, 0.0f);
-        updateModelMatrix(matrix, buffer);
-        rsgDrawMesh(gTorusMesh);
-        return;
-    }
-
-    if (numMeshes == 2) {
-        rsMatrixLoadTranslate(matrix, -1.6f, 0.0f, -7.5f);
-        rsMatrixRotate(matrix, gTorusRotation, 1.0f, 0.0f, 0.0f);
-        updateModelMatrix(matrix, buffer);
-        rsgDrawMesh(gTorusMesh);
-
-        rsMatrixLoadTranslate(matrix, 1.6f, 0.0f, -7.5f);
-        rsMatrixRotate(matrix, gTorusRotation, 1.0f, 0.0f, 0.0f);
-        updateModelMatrix(matrix, buffer);
-        rsgDrawMesh(gTorusMesh);
-        return;
-    }
-
-    float startX = -5.0f;
-    float startY = -1.5f;
-    float startZ = -15.0f;
-    float dist = 3.2f;
-
-    for (int h = 0; h < 4; h ++) {
-        for (int v = 0; v < 2; v ++) {
-            // Position our model on the screen
-            rsMatrixLoadTranslate(matrix, startX + dist * h, startY + dist * v, startZ);
-            rsMatrixRotate(matrix, gTorusRotation, 1.0f, 0.0f, 0.0f);
-            updateModelMatrix(matrix, buffer);
-            rsgDrawMesh(gTorusMesh);
-        }
-    }
-}
-
 // Quick hack to get some geometry numbers
 static void displaySimpleGeoSamples(bool useTexture, int numMeshes) {
-    rsgBindProgramVertex(gProgVertex);
-    rsgBindProgramRaster(gCullBack);
-    // Setup the projection matrix with 30 degree field of view
-    rs_matrix4x4 proj;
-    float aspect = (float)gRenderSurfaceW / (float)gRenderSurfaceH;
-    rsMatrixLoadPerspective(&proj, 30.0f, aspect, 0.1f, 100.0f);
-    rsgProgramVertexLoadProjectionMatrix(&proj);
-
-    // Fragment shader with texture
-    rsgBindProgramStore(gProgStoreBlendNoneDepth);
-    if (useTexture) {
-        rsgBindProgramFragment(gProgFragmentTexture);
-    } else {
-        rsgBindProgramFragment(gProgFragmentColor);
-        rsgProgramFragmentConstantColor(gProgFragmentColor, 0.1, 0.7, 0.1, 1);
-    }
-    rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
-    rsgBindTexture(gProgFragmentTexture, 0, gTexTorus);
-
-    // Apply a rotation to our mesh
-    gTorusRotation += 50.0f * gDt;
-    if (gTorusRotation > 360.0f) {
-        gTorusRotation -= 360.0f;
-    }
-
-    rs_matrix4x4 matrix;
-    drawToruses(numMeshes, &matrix, 0);
-}
-
-float gLight0Rotation = 0;
-float gLight1Rotation = 0;
-
-static void setupCustomShaderLights() {
-    float4 light0Pos = {-5.0f, 5.0f, -10.0f, 1.0f};
-    float4 light1Pos = {2.0f, 5.0f, 15.0f, 1.0f};
-    float4 light0DiffCol = {0.9f, 0.7f, 0.7f, 1.0f};
-    float4 light0SpecCol = {0.9f, 0.6f, 0.6f, 1.0f};
-    float4 light1DiffCol = {0.5f, 0.5f, 0.9f, 1.0f};
-    float4 light1SpecCol = {0.5f, 0.5f, 0.9f, 1.0f};
-
-    gLight0Rotation += 50.0f * gDt;
-    if (gLight0Rotation > 360.0f) {
-        gLight0Rotation -= 360.0f;
-    }
-    gLight1Rotation -= 50.0f * gDt;
-    if (gLight1Rotation > 360.0f) {
-        gLight1Rotation -= 360.0f;
-    }
-
-    rs_matrix4x4 l0Mat;
-    rsMatrixLoadRotate(&l0Mat, gLight0Rotation, 1.0f, 0.0f, 0.0f);
-    light0Pos = rsMatrixMultiply(&l0Mat, light0Pos);
-    rs_matrix4x4 l1Mat;
-    rsMatrixLoadRotate(&l1Mat, gLight1Rotation, 0.0f, 0.0f, 1.0f);
-    light1Pos = rsMatrixMultiply(&l1Mat, light1Pos);
-
-    // Set light 0 properties
-    gVSConstants->light0_Posision = light0Pos;
-    gVSConstants->light0_Diffuse = 1.0f;
-    gVSConstants->light0_Specular = 0.5f;
-    gVSConstants->light0_CosinePower = 10.0f;
-    // Set light 1 properties
-    gVSConstants->light1_Posision = light1Pos;
-    gVSConstants->light1_Diffuse = 1.0f;
-    gVSConstants->light1_Specular = 0.7f;
-    gVSConstants->light1_CosinePower = 25.0f;
-    rsgAllocationSyncAll(rsGetAllocation(gVSConstants));
-
-    // Update fragment shader constants
-    // Set light 0 colors
-    gFSConstants->light0_DiffuseColor = light0DiffCol;
-    gFSConstants->light0_SpecularColor = light0SpecCol;
-    // Set light 1 colors
-    gFSConstants->light1_DiffuseColor = light1DiffCol;
-    gFSConstants->light1_SpecularColor = light1SpecCol;
-    rsgAllocationSyncAll(rsGetAllocation(gFSConstants));
-
-    // Set light 0 properties for per pixel lighting
-    gFSConstPixel->light0_Posision = light0Pos;
-    gFSConstPixel->light0_Diffuse = 1.0f;
-    gFSConstPixel->light0_Specular = 0.5f;
-    gFSConstPixel->light0_CosinePower = 10.0f;
-    gFSConstPixel->light0_DiffuseColor = light0DiffCol;
-    gFSConstPixel->light0_SpecularColor = light0SpecCol;
-    // Set light 1 properties
-    gFSConstPixel->light1_Posision = light1Pos;
-    gFSConstPixel->light1_Diffuse = 1.0f;
-    gFSConstPixel->light1_Specular = 0.7f;
-    gFSConstPixel->light1_CosinePower = 25.0f;
-    gFSConstPixel->light1_DiffuseColor = light1DiffCol;
-    gFSConstPixel->light1_SpecularColor = light1SpecCol;
-    rsgAllocationSyncAll(rsGetAllocation(gFSConstPixel));
+    TestData testData;
+    testData.renderSurfaceW = gRenderSurfaceW;
+    testData.renderSurfaceH = gRenderSurfaceH;
+    testData.dt = gDt;
+    testData.user = 0;
+    testData.user1 = useTexture ? 1 : 0;
+    testData.user2 = numMeshes;
+    rsForEach(gTorusScript, gDummyAlloc, gDummyAlloc, &testData);
 }
 
 static void displayCustomShaderSamples(int numMeshes) {
-
-    // Update vertex shader constants
-    // Load model matrix
-    // Apply a rotation to our mesh
-    gTorusRotation += 50.0f * gDt;
-    if (gTorusRotation > 360.0f) {
-        gTorusRotation -= 360.0f;
-    }
-
-    // Setup the projection matrix
-    float aspect = (float)gRenderSurfaceW / (float)gRenderSurfaceH;
-    rsMatrixLoadPerspective(&gVSConstants->proj, 30.0f, aspect, 0.1f, 100.0f);
-    setupCustomShaderLights();
-
-    rsgBindProgramVertex(gProgVertexCustom);
-
-    // Fragment shader with texture
-    rsgBindProgramStore(gProgStoreBlendNoneDepth);
-    rsgBindProgramFragment(gProgFragmentCustom);
-    rsgBindSampler(gProgFragmentCustom, 0, gLinearClamp);
-    rsgBindTexture(gProgFragmentCustom, 0, gTexTorus);
-
-    // Use back face culling
-    rsgBindProgramRaster(gCullBack);
-
-    drawToruses(numMeshes, &gVSConstants->model, gVSConstants);
+    TestData testData;
+    testData.renderSurfaceW = gRenderSurfaceW;
+    testData.renderSurfaceH = gRenderSurfaceH;
+    testData.dt = gDt;
+    testData.user = 1;
+    testData.user1 = numMeshes;
+    rsForEach(gTorusScript, gDummyAlloc, gDummyAlloc, &testData);
 }
 
 static void displayPixelLightSamples(int numMeshes, bool heavyVertex) {
-
-    // Update vertex shader constants
-    // Load model matrix
-    // Apply a rotation to our mesh
-    gTorusRotation += 30.0f * gDt;
-    if (gTorusRotation > 360.0f) {
-        gTorusRotation -= 360.0f;
-    }
-
-    gVSConstPixel->time = rsUptimeMillis()*0.005;
-
-    // Setup the projection matrix
-    float aspect = (float)gRenderSurfaceW / (float)gRenderSurfaceH;
-    rsMatrixLoadPerspective(&gVSConstPixel->proj, 30.0f, aspect, 0.1f, 100.0f);
-    setupCustomShaderLights();
-
-    if (heavyVertex) {
-        rsgBindProgramVertex(gProgVertexPixelLightMove);
-    } else {
-        rsgBindProgramVertex(gProgVertexPixelLight);
-    }
-
-    // Fragment shader with texture
-    rsgBindProgramStore(gProgStoreBlendNoneDepth);
-    rsgBindProgramFragment(gProgFragmentPixelLight);
-    rsgBindSampler(gProgFragmentPixelLight, 0, gLinearClamp);
-    rsgBindTexture(gProgFragmentPixelLight, 0, gTexTorus);
-
-    // Use back face culling
-    rsgBindProgramRaster(gCullBack);
-
-    drawToruses(numMeshes, &gVSConstPixel->model, gVSConstPixel);
+    TestData testData;
+    testData.renderSurfaceW = gRenderSurfaceW;
+    testData.renderSurfaceH = gRenderSurfaceH;
+    testData.dt = gDt;
+    testData.user = 2;
+    testData.user1 = numMeshes;
+    testData.user2 = heavyVertex ? 1 : 0;
+    rsForEach(gTorusScript, gDummyAlloc, gDummyAlloc, &testData);
 }
 
 static void displayMultitextureSample(bool blend, int quadCount) {
@@ -862,6 +643,20 @@
     "UI test with live wallpaper",
 };
 
+static bool gIsDebugMode = false;
+void setDebugMode(int testNumber) {
+    gIsDebugMode = true;
+    benchMode = testNumber;
+    rsgClearAllRenderTargets();
+}
+
+void setBenchmarkMode() {
+    gIsDebugMode = false;
+    benchMode = 0;
+    runningLoops = 0;
+}
+
+
 void getTestName(int testIndex) {
     int bufferLen = rsAllocationGetDimX(rsGetAllocation(gStringBuffer));
     if (testIndex >= gMaxModes) {
@@ -932,7 +727,7 @@
         displaySingletexFill(true, 10);
         break;
     case 18:
-        displayMultitextureSample(true, 8);
+        displayMultitextureSample(true, 10);
         break;
     case 19:
         displayPixelLightSamples(1, false);
@@ -992,14 +787,7 @@
                          startX + width, startY, 0, 1, 1);
 }
 
-int root(void) {
-    gRenderSurfaceW = rsgGetWidth();
-    gRenderSurfaceH = rsgGetHeight();
-    rsgClearColor(0.2f, 0.2f, 0.2f, 1.0f);
-    rsgClearDepth(1.0f);
-    if(!checkInit()) {
-        return 1;
-    }
+static void benchmark() {
 
     gDt = 1.0f / 60.0f;
 
@@ -1045,8 +833,6 @@
 
     benchMode ++;
 
-    gTorusRotation = 0;
-
     if (benchMode == gMaxModes) {
         rsSendToClientBlocking(RS_MSG_RESULTS_READY, gResultBuffer, gMaxModes*sizeof(float));
         benchMode = 0;
@@ -1058,5 +844,30 @@
             sendMsgFlag = true;
         }
     }
+
+}
+
+static void debug() {
+    gDt = rsGetDt();
+
+    rsgFinish();
+    runTest(benchMode);
+}
+
+int root(void) {
+    gRenderSurfaceW = rsgGetWidth();
+    gRenderSurfaceH = rsgGetHeight();
+    rsgClearColor(0.2f, 0.2f, 0.2f, 1.0f);
+    rsgClearDepth(1.0f);
+    if(!checkInit()) {
+        return 1;
+    }
+
+    if (gIsDebugMode) {
+        debug();
+    } else {
+        benchmark();
+    }
+
     return 1;
 }
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/subtest_def.rsh b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/subtest_def.rsh
new file mode 100644
index 0000000..b635373
--- /dev/null
+++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/subtest_def.rsh
@@ -0,0 +1,28 @@
+// 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.perftest)
+
+typedef struct TestData_s {
+    int renderSurfaceW;
+    int renderSurfaceH;
+    float dt;
+    int user;
+    int user1;
+    int user2;
+    int user3;
+} TestData;
+
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/text_test.rs b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/text_test.rs
new file mode 100644
index 0000000..0df6b35
--- /dev/null
+++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/text_test.rs
@@ -0,0 +1,82 @@
+// 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.perftest)
+
+#include "rs_graphics.rsh"
+#include "subtest_def.rsh"
+
+rs_font gFontSans;
+rs_font gFontSerif;
+
+void init() {
+}
+
+static int gRenderSurfaceW = 1280;
+static int gRenderSurfaceH = 720;
+
+static const char *sampleText = "This is a sample of small text for performace";
+// Offsets for multiple layer of text
+static int textOffsets[] = { 0,  0, -5, -5, 5,  5, -8, -8, 8,  8};
+static float textColors[] = {1.0f, 1.0f, 1.0f, 1.0f,
+                             0.5f, 0.7f, 0.5f, 1.0f,
+                             0.7f, 0.5f, 0.5f, 1.0f,
+                             0.5f, 0.5f, 0.7f, 1.0f,
+                             0.5f, 0.6f, 0.7f, 1.0f,
+};
+
+static void displayFontSamples(int fillNum) {
+
+    rs_font fonts[5];
+    fonts[0] = gFontSans;
+    fonts[1] = gFontSerif;
+    fonts[2] = gFontSans;
+    fonts[3] = gFontSerif;
+    fonts[4] = gFontSans;
+
+    uint width = gRenderSurfaceW;
+    uint height = gRenderSurfaceH;
+    int left = 0, right = 0, top = 0, bottom = 0;
+    rsgMeasureText(sampleText, &left, &right, &top, &bottom);
+
+    int textHeight = top - bottom;
+    int textWidth = right - left;
+    int numVerticalLines = height / textHeight;
+    int yPos = top;
+
+    int xOffset = 0, yOffset = 0;
+    for(int fillI = 0; fillI < fillNum; fillI ++) {
+        rsgBindFont(fonts[fillI]);
+        xOffset = textOffsets[fillI * 2];
+        yOffset = textOffsets[fillI * 2 + 1];
+        float *colPtr = textColors + fillI * 4;
+        rsgFontColor(colPtr[0], colPtr[1], colPtr[2], colPtr[3]);
+        for (int h = 0; h < 4; h ++) {
+            yPos = top + yOffset;
+            for (int v = 0; v < numVerticalLines; v ++) {
+                rsgDrawText(sampleText, xOffset + textWidth * h, yPos);
+                yPos += textHeight;
+            }
+        }
+    }
+}
+
+void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32_t y) {
+    TestData *testData = (TestData*)usrData;
+    gRenderSurfaceW = testData->renderSurfaceW;
+    gRenderSurfaceH = testData->renderSurfaceH;
+    displayFontSamples(testData->user);
+}
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/torus_test.rs b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/torus_test.rs
new file mode 100644
index 0000000..26d5680
--- /dev/null
+++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/torus_test.rs
@@ -0,0 +1,283 @@
+// 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.perftest)
+
+#include "rs_graphics.rsh"
+#include "subtest_def.rsh"
+#include "shader_def.rsh"
+
+rs_program_vertex gProgVertex;
+rs_program_fragment gProgFragmentColor;
+rs_program_fragment gProgFragmentTexture;
+
+rs_program_store gProgStoreBlendNoneDepth;
+rs_mesh gTorusMesh;
+
+rs_program_raster gCullBack;
+rs_program_raster gCullFront;
+
+// Custom vertex shader compunents
+VertexShaderConstants *gVSConstants;
+FragentShaderConstants *gFSConstants;
+VertexShaderConstants3 *gVSConstPixel;
+FragentShaderConstants3 *gFSConstPixel;
+
+// Custom shaders we use for lighting
+rs_program_vertex gProgVertexCustom;
+rs_program_fragment gProgFragmentCustom;
+
+rs_sampler gLinearClamp;
+rs_allocation gTexTorus;
+
+rs_program_vertex gProgVertexPixelLight;
+rs_program_vertex gProgVertexPixelLightMove;
+rs_program_fragment gProgFragmentPixelLight;
+
+static float gDt = 0.0f;
+
+static int gRenderSurfaceW;
+static int gRenderSurfaceH;
+
+
+static float gTorusRotation = 0;
+static void updateModelMatrix(rs_matrix4x4 *matrix, void *buffer) {
+    if (buffer == 0) {
+        rsgProgramVertexLoadModelMatrix(matrix);
+    } else {
+        rsgAllocationSyncAll(rsGetAllocation(buffer));
+    }
+}
+
+static void drawToruses(int numMeshes, rs_matrix4x4 *matrix, void *buffer) {
+
+    if (numMeshes == 1) {
+        rsMatrixLoadTranslate(matrix, 0.0f, 0.0f, -7.5f);
+        rsMatrixRotate(matrix, gTorusRotation, 1.0f, 0.0f, 0.0f);
+        updateModelMatrix(matrix, buffer);
+        rsgDrawMesh(gTorusMesh);
+        return;
+    }
+
+    if (numMeshes == 2) {
+        rsMatrixLoadTranslate(matrix, -1.6f, 0.0f, -7.5f);
+        rsMatrixRotate(matrix, gTorusRotation, 1.0f, 0.0f, 0.0f);
+        updateModelMatrix(matrix, buffer);
+        rsgDrawMesh(gTorusMesh);
+
+        rsMatrixLoadTranslate(matrix, 1.6f, 0.0f, -7.5f);
+        rsMatrixRotate(matrix, gTorusRotation, 1.0f, 0.0f, 0.0f);
+        updateModelMatrix(matrix, buffer);
+        rsgDrawMesh(gTorusMesh);
+        return;
+    }
+
+    float startX = -5.0f;
+    float startY = -1.5f;
+    float startZ = -15.0f;
+    float dist = 3.2f;
+
+    for (int h = 0; h < 4; h ++) {
+        for (int v = 0; v < 2; v ++) {
+            // Position our model on the screen
+            rsMatrixLoadTranslate(matrix, startX + dist * h, startY + dist * v, startZ);
+            rsMatrixRotate(matrix, gTorusRotation, 1.0f, 0.0f, 0.0f);
+            updateModelMatrix(matrix, buffer);
+            rsgDrawMesh(gTorusMesh);
+        }
+    }
+}
+
+
+// Quick hack to get some geometry numbers
+static void displaySimpleGeoSamples(bool useTexture, int numMeshes) {
+    rsgBindProgramVertex(gProgVertex);
+    rsgBindProgramRaster(gCullBack);
+    // Setup the projection matrix with 30 degree field of view
+    rs_matrix4x4 proj;
+    float aspect = (float)gRenderSurfaceW / (float)gRenderSurfaceH;
+    rsMatrixLoadPerspective(&proj, 30.0f, aspect, 0.1f, 100.0f);
+    rsgProgramVertexLoadProjectionMatrix(&proj);
+
+    // Fragment shader with texture
+    rsgBindProgramStore(gProgStoreBlendNoneDepth);
+    if (useTexture) {
+        rsgBindProgramFragment(gProgFragmentTexture);
+    } else {
+        rsgBindProgramFragment(gProgFragmentColor);
+        rsgProgramFragmentConstantColor(gProgFragmentColor, 0.1, 0.7, 0.1, 1);
+    }
+    rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
+    rsgBindTexture(gProgFragmentTexture, 0, gTexTorus);
+
+    // Apply a rotation to our mesh
+    gTorusRotation += 50.0f * gDt;
+    if (gTorusRotation > 360.0f) {
+        gTorusRotation -= 360.0f;
+    }
+
+    rs_matrix4x4 matrix;
+    drawToruses(numMeshes, &matrix, 0);
+}
+
+float gLight0Rotation = 0;
+float gLight1Rotation = 0;
+
+static void setupCustomShaderLights() {
+    float4 light0Pos = {-5.0f, 5.0f, -10.0f, 1.0f};
+    float4 light1Pos = {2.0f, 5.0f, 15.0f, 1.0f};
+    float4 light0DiffCol = {0.9f, 0.7f, 0.7f, 1.0f};
+    float4 light0SpecCol = {0.9f, 0.6f, 0.6f, 1.0f};
+    float4 light1DiffCol = {0.5f, 0.5f, 0.9f, 1.0f};
+    float4 light1SpecCol = {0.5f, 0.5f, 0.9f, 1.0f};
+
+    gLight0Rotation += 50.0f * gDt;
+    if (gLight0Rotation > 360.0f) {
+        gLight0Rotation -= 360.0f;
+    }
+    gLight1Rotation -= 50.0f * gDt;
+    if (gLight1Rotation > 360.0f) {
+        gLight1Rotation -= 360.0f;
+    }
+
+    rs_matrix4x4 l0Mat;
+    rsMatrixLoadRotate(&l0Mat, gLight0Rotation, 1.0f, 0.0f, 0.0f);
+    light0Pos = rsMatrixMultiply(&l0Mat, light0Pos);
+    rs_matrix4x4 l1Mat;
+    rsMatrixLoadRotate(&l1Mat, gLight1Rotation, 0.0f, 0.0f, 1.0f);
+    light1Pos = rsMatrixMultiply(&l1Mat, light1Pos);
+
+    // Set light 0 properties
+    gVSConstants->light0_Posision = light0Pos;
+    gVSConstants->light0_Diffuse = 1.0f;
+    gVSConstants->light0_Specular = 0.5f;
+    gVSConstants->light0_CosinePower = 10.0f;
+    // Set light 1 properties
+    gVSConstants->light1_Posision = light1Pos;
+    gVSConstants->light1_Diffuse = 1.0f;
+    gVSConstants->light1_Specular = 0.7f;
+    gVSConstants->light1_CosinePower = 25.0f;
+    rsgAllocationSyncAll(rsGetAllocation(gVSConstants));
+
+    // Update fragment shader constants
+    // Set light 0 colors
+    gFSConstants->light0_DiffuseColor = light0DiffCol;
+    gFSConstants->light0_SpecularColor = light0SpecCol;
+    // Set light 1 colors
+    gFSConstants->light1_DiffuseColor = light1DiffCol;
+    gFSConstants->light1_SpecularColor = light1SpecCol;
+    rsgAllocationSyncAll(rsGetAllocation(gFSConstants));
+
+    // Set light 0 properties for per pixel lighting
+    gFSConstPixel->light0_Posision = light0Pos;
+    gFSConstPixel->light0_Diffuse = 1.0f;
+    gFSConstPixel->light0_Specular = 0.5f;
+    gFSConstPixel->light0_CosinePower = 10.0f;
+    gFSConstPixel->light0_DiffuseColor = light0DiffCol;
+    gFSConstPixel->light0_SpecularColor = light0SpecCol;
+    // Set light 1 properties
+    gFSConstPixel->light1_Posision = light1Pos;
+    gFSConstPixel->light1_Diffuse = 1.0f;
+    gFSConstPixel->light1_Specular = 0.7f;
+    gFSConstPixel->light1_CosinePower = 25.0f;
+    gFSConstPixel->light1_DiffuseColor = light1DiffCol;
+    gFSConstPixel->light1_SpecularColor = light1SpecCol;
+    rsgAllocationSyncAll(rsGetAllocation(gFSConstPixel));
+}
+
+static void displayCustomShaderSamples(int numMeshes) {
+
+    // Update vertex shader constants
+    // Load model matrix
+    // Apply a rotation to our mesh
+    gTorusRotation += 50.0f * gDt;
+    if (gTorusRotation > 360.0f) {
+        gTorusRotation -= 360.0f;
+    }
+
+    // Setup the projection matrix
+    float aspect = (float)gRenderSurfaceW / (float)gRenderSurfaceH;
+    rsMatrixLoadPerspective(&gVSConstants->proj, 30.0f, aspect, 0.1f, 100.0f);
+    setupCustomShaderLights();
+
+    rsgBindProgramVertex(gProgVertexCustom);
+
+    // Fragment shader with texture
+    rsgBindProgramStore(gProgStoreBlendNoneDepth);
+    rsgBindProgramFragment(gProgFragmentCustom);
+    rsgBindSampler(gProgFragmentCustom, 0, gLinearClamp);
+    rsgBindTexture(gProgFragmentCustom, 0, gTexTorus);
+
+    // Use back face culling
+    rsgBindProgramRaster(gCullBack);
+
+    drawToruses(numMeshes, &gVSConstants->model, gVSConstants);
+}
+
+static void displayPixelLightSamples(int numMeshes, bool heavyVertex) {
+
+    // Update vertex shader constants
+    // Load model matrix
+    // Apply a rotation to our mesh
+    gTorusRotation += 30.0f * gDt;
+    if (gTorusRotation > 360.0f) {
+        gTorusRotation -= 360.0f;
+    }
+
+    gVSConstPixel->time = rsUptimeMillis()*0.005;
+
+    // Setup the projection matrix
+    float aspect = (float)gRenderSurfaceW / (float)gRenderSurfaceH;
+    rsMatrixLoadPerspective(&gVSConstPixel->proj, 30.0f, aspect, 0.1f, 100.0f);
+    setupCustomShaderLights();
+
+    if (heavyVertex) {
+        rsgBindProgramVertex(gProgVertexPixelLightMove);
+    } else {
+        rsgBindProgramVertex(gProgVertexPixelLight);
+    }
+
+    // Fragment shader with texture
+    rsgBindProgramStore(gProgStoreBlendNoneDepth);
+    rsgBindProgramFragment(gProgFragmentPixelLight);
+    rsgBindSampler(gProgFragmentPixelLight, 0, gLinearClamp);
+    rsgBindTexture(gProgFragmentPixelLight, 0, gTexTorus);
+
+    // Use back face culling
+    rsgBindProgramRaster(gCullBack);
+
+    drawToruses(numMeshes, &gVSConstPixel->model, gVSConstPixel);
+}
+
+
+void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32_t y) {
+    TestData *testData = (TestData*)usrData;
+    gRenderSurfaceW = testData->renderSurfaceW;
+    gRenderSurfaceH = testData->renderSurfaceH;
+    gDt = testData->dt;
+
+    switch(testData->user) {
+        case 0:
+            displaySimpleGeoSamples(testData->user1 == 1 ? true : false, testData->user2);
+            break;
+        case 1:
+            displayCustomShaderSamples(testData->user1);
+            break;
+        case 2:
+            displayPixelLightSamples(testData->user1, testData->user2 == 1 ? true : false);
+            break;
+    }
+}