Merge "Build cert chain in KeyChain.getCertificateChain"
diff --git a/api/current.txt b/api/current.txt
index 309e747..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
@@ -3453,6 +3453,7 @@
     field public static final java.lang.String SUGGEST_COLUMN_INTENT_DATA = "suggest_intent_data";
     field public static final java.lang.String SUGGEST_COLUMN_INTENT_DATA_ID = "suggest_intent_data_id";
     field public static final java.lang.String SUGGEST_COLUMN_INTENT_EXTRA_DATA = "suggest_intent_extra_data";
+    field public static final java.lang.String SUGGEST_COLUMN_LAST_ACCESS_HINT = "suggest_last_access_hint";
     field public static final java.lang.String SUGGEST_COLUMN_QUERY = "suggest_intent_query";
     field public static final java.lang.String SUGGEST_COLUMN_SHORTCUT_ID = "suggest_shortcut_id";
     field public static final java.lang.String SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING = "suggest_spinner_while_refreshing";
@@ -22074,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
@@ -22263,6 +22265,7 @@
     method public static deprecated int getTouchSlop();
     method public static deprecated int getWindowTouchSlop();
     method public static long getZoomControlsTimeout();
+    method public boolean hasPermanentMenuKey();
   }
 
   public class ViewDebug {
@@ -24890,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();
@@ -24912,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;
@@ -24940,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/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 85a2fa8..7274362 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -329,6 +329,15 @@
     public final static String SUGGEST_COLUMN_FLAGS = "suggest_flags";
 
     /**
+     * Column name for suggestions cursor. <i>Optional.</i> This column may be
+     * used to specify the time in (@link System#currentTimeMillis
+     * System.currentTImeMillis()} (wall time in UTC) when an item was last
+     * accessed within the results-providing application. If set, this may be
+     * used to show more-recently-used items first.
+     */
+    public final static String SUGGEST_COLUMN_LAST_ACCESS_HINT = "suggest_last_access_hint";
+
+    /**
      * Column value for suggestion column {@link #SUGGEST_COLUMN_SHORTCUT_ID} when a suggestion
      * should not be stored as a shortcut in global search.
      */
diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl
index ae9aa05..0548250 100644
--- a/core/java/android/net/INetworkStatsService.aidl
+++ b/core/java/android/net/INetworkStatsService.aidl
@@ -33,4 +33,7 @@
     /** Return usage summary per UID for traffic that matches template. */
     NetworkStats getSummaryForAllUid(in NetworkTemplate template, long start, long end, boolean includeTags);
 
+    /** Force update of statistics. */
+    void forceUpdate();
+
 }
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index ff6e220..dd2945c 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -279,10 +279,17 @@
         return (long) (start + (r.nextFloat() * (end - start)));
     }
 
-    public void dump(String prefix, PrintWriter pw) {
+    public void dump(String prefix, PrintWriter pw, boolean fullHistory) {
         pw.print(prefix);
         pw.print("NetworkStatsHistory: bucketDuration="); pw.println(bucketDuration);
-        for (int i = 0; i < bucketCount; i++) {
+
+        final int start = fullHistory ? 0 : Math.max(0, bucketCount - 32);
+        if (start > 0) {
+            pw.print(prefix);
+            pw.print("  (omitting "); pw.print(start); pw.println(" buckets)");
+        }
+
+        for (int i = start; i < bucketCount; i++) {
             pw.print(prefix);
             pw.print("  bucketStart="); pw.print(bucketStart[i]);
             pw.print(" rx="); pw.print(rx[i]);
@@ -293,7 +300,7 @@
     @Override
     public String toString() {
         final CharArrayWriter writer = new CharArrayWriter();
-        dump("", new PrintWriter(writer));
+        dump("", new PrintWriter(writer), false);
         return writer.toString();
     }
 
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index c9b6121..fcf4796 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -241,6 +241,4 @@
      */
     int getInterfaceTxThrottle(String iface);
 
-    void setBandwidthControlEnabled(boolean enabled);
-
 }
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 ab0cb50..d99c760 100644
--- a/core/java/android/provider/VoicemailContract.java
+++ b/core/java/android/provider/VoicemailContract.java
@@ -52,18 +52,27 @@
 
     /** 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/");
+    /**
+     * 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.
+     */
+    public static final String PARAM_KEY_SOURCE_PACKAGE = "source_package";
 
     // TODO: Move ACTION_NEW_VOICEMAIL to the Intent class.
     /** Broadcast intent when a new voicemail record is inserted. */
     public static final String ACTION_NEW_VOICEMAIL = "android.intent.action.NEW_VOICEMAIL";
-
     /**
      * Extra included in {@value Intent#ACTION_PROVIDER_CHANGED} and
      * {@value #ACTION_NEW_VOICEMAIL} broadcast intents to indicate if the receiving
@@ -71,15 +80,27 @@
      */
     public static final String EXTRA_SELF_CHANGE = "com.android.voicemail.extra.SELF_CHANGE";
 
-    /** The mime type for a collection of voicemails. */
-    public static final String DIR_TYPE =
-            "vnd.android.cursor.dir/voicemails";
+    /**
+     * The mime type for a collection of voicemails.
+     * @deprecated */
+    public static final String DIR_TYPE = "vnd.android.cursor.dir/voicemails";
 
+    /** 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>
@@ -143,5 +164,101 @@
          * @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() {
+        }
+        /**
+         * The package name of the voicemail source. There can only be a one entry per source.
+         * <P>Type: TEXT</P>
+         */
+        public static final String SOURCE_PACKAGE = "source_package";
+        /**
+         * The URI to call to invoke source specific voicemail settings screen. On a user request
+         * to setup voicemail an intent with action VIEW with this URI will be fired by the system.
+         * <P>Type: TEXT</P>
+         */
+        public static final String SETTINGS_URI = "settings_uri";
+        /**
+         * The URI to call when the user requests to directly access the voicemail from the remote
+         * server. In case of an IVR voicemail system this is typically set to the the voicemail
+         * number specified using a tel:/ URI.
+         * <P>Type: TEXT</P>
+         */
+        public static final String VOICEMAIL_ACCESS_URI = "voicemail_access_uri";
+        /**
+         * The configuration state of the voicemail source.
+         * <P> Possible values:
+         * {@link #CONFIGURATION_STATE_OK},
+         * {@link #CONFIGURATION_STATE_NOT_CONFIGURED},
+         * {@link #CONFIGURATION_STATE_CAN_BE_CONFIGURED}
+         * <P>Type: INTEGER</P>
+         */
+        public static final String CONFIGURATION_STATE = "configuration_state";
+        public static final int CONFIGURATION_STATE_OK = 0;
+        public static final int CONFIGURATION_STATE_NOT_CONFIGURED = 1;
+        /**
+         * This state must be used when the source has verified that the current user can be
+         * upgraded to visual voicemail and would like to show a set up invitation message.
+         */
+        public static final int CONFIGURATION_STATE_CAN_BE_CONFIGURED = 2;
+        /**
+         * The data channel state of the voicemail source. This the channel through which the source
+         * pulls voicemail data from a remote server.
+         * <P> Possible values:
+         * {@link #DATA_CHANNEL_STATE_OK},
+         * {@link #DATA_CHANNEL_STATE_NO_CONNECTION}
+         * </P>
+         * <P>Type: INTEGER</P>
+         */
+        public static final String DATA_CHANNEL_STATE = "data_channel_state";
+        public static final int DATA_CHANNEL_STATE_OK = 0;
+        public static final int DATA_CHANNEL_STATE_NO_CONNECTION = 1;
+        /**
+         * The notification channel state of the voicemail source. This is the channel through which
+         * the source gets notified of new voicemails on the remote server.
+         * <P> Possible values:
+         * {@link #NOTIFICATION_CHANNEL_STATE_OK},
+         * {@link #NOTIFICATION_CHANNEL_STATE_NO_CONNECTION},
+         * {@link #NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING}
+         * </P>
+         * <P>Type: INTEGER</P>
+         */
+        public static final String NOTIFICATION_CHANNEL_STATE = "notification_channel_state";
+        public static final int NOTIFICATION_CHANNEL_STATE_OK = 0;
+        public static final int NOTIFICATION_CHANNEL_STATE_NO_CONNECTION = 1;
+        /**
+         * Use this state when the notification can only tell that there are pending messages on
+         * the server but no details of the sender/time etc are known.
+         */
+        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/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java
index ca2212c..8a6fdb4 100644
--- a/core/java/android/server/BluetoothA2dpService.java
+++ b/core/java/android/server/BluetoothA2dpService.java
@@ -517,6 +517,7 @@
             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
             intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
             intent.putExtra(BluetoothProfile.EXTRA_STATE, state);
+            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
             mContext.sendBroadcast(intent, BLUETOOTH_PERM);
 
             if (DBG) log("A2DP state : device: " + device + " State:" + prevState + "->" + state);
@@ -530,6 +531,7 @@
         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
         intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
         intent.putExtra(BluetoothProfile.EXTRA_STATE, state);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
         mContext.sendBroadcast(intent, BLUETOOTH_PERM);
 
         if (DBG) log("A2DP Playing state : device: " + device + " State:" + prevState + "->" + state);
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/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index f3a5050..dbcbd6e 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -19,6 +19,8 @@
 import android.app.AppGlobals;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.RemoteException;
 import android.provider.Settings;
 import android.util.DisplayMetrics;
 import android.util.SparseArray;
@@ -219,6 +221,9 @@
     private final int mOverscrollDistance;
     private final int mOverflingDistance;
 
+    private boolean sHasPermanentMenuKey;
+    private boolean sHasPermanentMenuKeySet;
+
     private static final SparseArray<ViewConfiguration> sConfigurations =
             new SparseArray<ViewConfiguration>(2);
 
@@ -254,11 +259,12 @@
      * @see android.util.DisplayMetrics
      */
     private ViewConfiguration(Context context) {
-        final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+        final Resources res = context.getResources();
+        final DisplayMetrics metrics = res.getDisplayMetrics();
+        final Configuration config = res.getConfiguration();
         final float density = metrics.density;
         final float sizeAndDensity;
-        if (context.getResources().getConfiguration().isLayoutSizeAtLeast(
-                Configuration.SCREENLAYOUT_SIZE_XLARGE)) {
+        if (config.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_XLARGE)) {
             sizeAndDensity = density * 1.5f;
         } else {
             sizeAndDensity = density;
@@ -280,6 +286,17 @@
 
         mOverscrollDistance = (int) (sizeAndDensity * OVERSCROLL_DISTANCE + 0.5f);
         mOverflingDistance = (int) (sizeAndDensity * OVERFLING_DISTANCE + 0.5f);
+
+        if (!sHasPermanentMenuKeySet) {
+            IWindowManager wm = Display.getWindowManager();
+            try {
+                sHasPermanentMenuKey = wm.canStatusBarHide() && !res.getBoolean(
+                        com.android.internal.R.bool.config_showNavigationBar);
+                sHasPermanentMenuKeySet = true;
+            } catch (RemoteException ex) {
+                sHasPermanentMenuKey = false;
+            }
+        }
     }
 
     /**
@@ -640,4 +657,20 @@
     public static float getScrollFriction() {
         return SCROLL_FRICTION;
     }
+
+    /**
+     * Report if the device has a permanent menu key available to the user.
+     *
+     * <p>As of Android 3.0, devices may not have a permanent menu key available.
+     * Apps should use the action bar to present menu options to users.
+     * However, there are some apps where the action bar is inappropriate
+     * or undesirable. This method may be used to detect if a menu key is present.
+     * If not, applications should provide another on-screen affordance to access
+     * functionality.
+     *
+     * @return true if a permanent menu key is present, false otherwise.
+     */
+    public boolean hasPermanentMenuKey() {
+        return sHasPermanentMenuKey;
+    }
 }
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/webkit/L10nUtils.java b/core/java/android/webkit/L10nUtils.java
index 5b4fb1d..4c42cde 100644
--- a/core/java/android/webkit/L10nUtils.java
+++ b/core/java/android/webkit/L10nUtils.java
@@ -70,7 +70,11 @@
         com.android.internal.R.string.autofill_expiration_month_re,         // IDS_AUTOFILL_EXPIRATION_MONTH_RE
         com.android.internal.R.string.autofill_expiration_date_re,          // IDS_AUTOFILL_EXPIRATION_DATE_RE
         com.android.internal.R.string.autofill_card_ignored_re,             // IDS_AUTOFILL_CARD_IGNORED_RE
-        com.android.internal.R.string.autofill_fax_re                       // IDS_AUTOFILL_FAX_RE
+        com.android.internal.R.string.autofill_fax_re,                      // IDS_AUTOFILL_FAX_RE
+        com.android.internal.R.string.autofill_country_code_re,             // IDS_AUTOFILL_COUNTRY_CODE_RE
+        com.android.internal.R.string.autofill_area_code_notext_re,         // IDS_AUTOFILL_AREA_CODE_NOTEXT_RE
+        com.android.internal.R.string.autofill_phone_prefix_separator_re,   // IDS_AUTOFILL_PHONE_PREFIX_SEPARATOR_RE
+        com.android.internal.R.string.autofill_phone_suffix_separator_re    // IDS_AUTOFILL_PHONE_SUFFIX_SEPARATOR_RE
     };
 
     private static Context mApplicationContext;
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/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java
index 773be5b..572a1d7 100644
--- a/core/java/com/android/internal/net/VpnConfig.java
+++ b/core/java/com/android/internal/net/VpnConfig.java
@@ -23,6 +23,8 @@
 import android.os.Parcelable;
 import android.os.SystemClock;
 
+import java.util.List;
+
 /**
  * A simple container used to carry information in VpnBuilder, VpnDialogs,
  * and com.android.server.connectivity.Vpn. Internal use only.
@@ -33,12 +35,6 @@
 
     public static final String ACTION_VPN_REVOKED = "android.net.vpn.action.REVOKED";
 
-    public static void enforceCallingPackage(String packageName) {
-        if (!"com.android.vpndialogs".equals(packageName)) {
-            throw new SecurityException("Unauthorized Caller");
-        }
-    }
-
     public static Intent getIntentForConfirmation() {
         Intent intent = new Intent();
         intent.setClassName("com.android.vpndialogs", "com.android.vpndialogs.ConfirmDialog");
@@ -58,11 +54,12 @@
     public String packageName;
     public String sessionName;
     public String interfaceName;
-    public String configureActivity;
+    public PendingIntent configureIntent;
     public int mtu = -1;
     public String addresses;
     public String routes;
-    public String dnsServers;
+    public List<String> dnsServers;
+    public List<String> searchDomains;
     public long startTime = -1;
 
     @Override
@@ -75,11 +72,12 @@
         out.writeString(packageName);
         out.writeString(sessionName);
         out.writeString(interfaceName);
-        out.writeString(configureActivity);
+        out.writeParcelable(configureIntent, flags);
         out.writeInt(mtu);
         out.writeString(addresses);
         out.writeString(routes);
-        out.writeString(dnsServers);
+        out.writeStringList(dnsServers);
+        out.writeStringList(searchDomains);
         out.writeLong(startTime);
     }
 
@@ -91,11 +89,12 @@
             config.packageName = in.readString();
             config.sessionName = in.readString();
             config.interfaceName = in.readString();
-            config.configureActivity = in.readString();
+            config.configureIntent = in.readParcelable(null);
             config.mtu = in.readInt();
             config.addresses = in.readString();
             config.routes = in.readString();
-            config.dnsServers = in.readString();
+            config.dnsServers = in.createStringArrayList();
+            config.searchDomains = in.createStringArrayList();
             config.startTime = in.readLong();
             return config;
         }
diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
index 322a854..2fec9cd 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
@@ -26,6 +26,7 @@
 import android.view.SoundEffectConstants;
 import android.view.View;
 import android.view.View.MeasureSpec;
+import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.widget.ImageButton;
 
@@ -69,9 +70,7 @@
         final Resources res = context.getResources();
 
         if (!mReserveOverflowSet) {
-            // TODO Use the no-buttons specifier instead here
-            mReserveOverflow = res.getConfiguration()
-                    .isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE);
+            mReserveOverflow = !ViewConfiguration.get(context).hasPermanentMenuKey();
         }
 
         if (!mWidthLimitSet) {
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/config.xml b/core/res/res/values/config.xml
index 827153e..2c10b3d 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -658,4 +658,8 @@
          This is intended to allow packaging drivers or tools for installation on a PC. -->
     <string translatable="false" name="config_isoImagePath"></string>
 
+    <!-- Whether a software navigation bar should be shown. NOTE: in the future this may be
+         autodetected from the Configuration. -->
+    <bool name="config_showNavigationBar">false</bool>
+
 </resources>
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 88ed9c6b..c8b3b4f 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2073,6 +2073,18 @@
     <!-- Do not translate. Regex used by AutoFill. -->
     <string name="autofill_fax_re">fax<!-- fr-FR -->|télécopie|telecopie<!-- ja-JP -->|ファックス<!-- ru -->|факс<!-- zh-CN -->|传真<!-- zh-TW -->|傳真</string>
 
+    <!-- Do not translate. Regex used by AutoFill. -->
+    <string name="autofill_country_code_re">country.*code|ccode|_cc</string>
+
+    <!-- Do not translate. Regex used by AutoFill. -->
+    <string name="autofill_area_code_notext_re">^\($</string>
+
+    <!-- Do not translate. Regex used by AutoFill. -->
+    <string name="autofill_phone_prefix_separator_re">^-$|^\)$</string>
+
+    <!-- Do not translate. Regex used by AutoFill. -->
+    <string name="autofill_phone_suffix_separator_re">^-$</string>
+
     <!-- Title of an application permission, listed so the user can choose whether
         they want to allow the application to do this. -->
     <string name="permlab_readHistoryBookmarks">read Browser\'s history and bookmarks</string>
@@ -2613,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. -->
@@ -2788,10 +2804,10 @@
     <string name="l2tp_ipsec_psk_vpn_description">Pre-shared key based L2TP/IPSec VPN</string>
     <string name="l2tp_ipsec_crt_vpn_description">Certificate based L2TP/IPSec VPN</string>
 
-    <!-- Ticker text to show when VPN is active. -->
-    <string name="vpn_ticker"><xliff:g id="app" example="FooVPN client">%s</xliff:g> is activating VPN...</string>
     <!-- The title of the notification when VPN is active. -->
-    <string name="vpn_title">VPN is activated by <xliff:g id="app" example="FooVPN client">%s</xliff:g></string>
+    <string name="vpn_title">VPN is activated.</string>
+    <!-- The title of the notification when VPN is active with an application name. -->
+    <string name="vpn_title_long">VPN is activated by <xliff:g id="app" example="FooVPN client">%s</xliff:g></string>
     <!-- The text of the notification when VPN is active. -->
     <string name="vpn_text">Tap to manage the network.</string>
     <!-- The text of the notification when VPN is active with a session name. -->
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/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index 90a7ac2..1647ff3 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -174,12 +174,15 @@
      * Retrieve the timestamp associated with the texture image set by the most recent call to
      * updateTexImage.
      *
-     * This timestamp is in nanoseconds, and is guaranteed to be monotonically increasing. The
+     * This timestamp is in nanoseconds, and is normally monotonically increasing. The timestamp
+     * should be unaffected by time-of-day adjustments, and for a camera should be strictly
+     * monotonic but for a MediaPlayer may be reset when the position is set.  The
      * specific meaning and zero point of the timestamp depends on the source providing images to
      * the SurfaceTexture. Unless otherwise specified by the image source, timestamps cannot
      * generally be compared across SurfaceTexture instances, or across multiple program
      * invocations. It is mostly useful for determining time offsets between subsequent frames.
      */
+
     public long getTimestamp() {
         return nativeGetTimestamp();
     }
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h
index 18e8a5f..4328d3c 100644
--- a/include/media/MediaPlayerInterface.h
+++ b/include/media/MediaPlayerInterface.h
@@ -103,6 +103,10 @@
     virtual status_t    initCheck() = 0;
     virtual bool        hardwareOutput() = 0;
 
+    virtual status_t    setUID(uid_t uid) {
+        return INVALID_OPERATION;
+    }
+
     virtual status_t    setDataSource(
             const char *url,
             const KeyedVector<String8, String8> *headers = NULL) = 0;
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/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
index ffc3346..29dec63 100755
--- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -205,7 +205,7 @@
             mNiNotification.defaults &= ~Notification.DEFAULT_SOUND;
         }        
 
-        mNiNotification.flags = Notification.FLAG_ONGOING_EVENT;
+        mNiNotification.flags = Notification.FLAG_ONGOING_EVENT | Notification.FLAG_AUTO_CANCEL;
         mNiNotification.tickerText = getNotifTicker(notif, mContext);
 
         // if not to popup dialog immediately, pending intent will open the dialog
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 33312d1..482b437 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -611,15 +611,11 @@
      * needed.  Not calling this method when playing back a video will
      * result in only the audio track being played.
      *
-     * @param sh the SurfaceHolder to use for video display
-     */
-    /*
-     * This portion of comment has a non-Javadoc prefix so as not to refer to a
-     * hidden method. When unhidden, merge it with the previous javadoc comment.
-     *
      * Either a surface or surface texture must be set if a display or video sink
      * is needed.  Not calling this method or {@link #setTexture(SurfaceTexture)}
      * when playing back a video will result in only the audio track being played.
+     *
+     * @param sh the SurfaceHolder to use for video display
      */
     public void setDisplay(SurfaceHolder sh) {
         mSurfaceHolder = sh;
@@ -648,7 +644,8 @@
      * SurfaceTexture set as the video sink have an unspecified zero point,
      * and cannot be directly compared between different media sources or different
      * instances of the same media source, or across multiple runs of the same
-     * program.
+     * program.  The timestamp is normally monotonically increasing and unaffected
+     * by time-of-day adjustments, but is reset when the position is set.
      */
     public void setTexture(SurfaceTexture st) {
         ParcelSurfaceTexture pst = null;
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index a77dff1..1e7c969 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -250,7 +250,11 @@
         const KeyedVector<String8, String8> *headers, int audioSessionId)
 {
     int32_t connId = android_atomic_inc(&mNextConnId);
-    sp<Client> c = new Client(this, pid, connId, client, audioSessionId);
+
+    sp<Client> c = new Client(
+            this, pid, connId, client, audioSessionId,
+            IPCThreadState::self()->getCallingUid());
+
     LOGV("Create new client(%d) from pid %d, url=%s, connId=%d, audioSessionId=%d",
             connId, pid, url, connId, audioSessionId);
     if (NO_ERROR != c->setDataSource(url, headers))
@@ -268,7 +272,11 @@
         int fd, int64_t offset, int64_t length, int audioSessionId)
 {
     int32_t connId = android_atomic_inc(&mNextConnId);
-    sp<Client> c = new Client(this, pid, connId, client, audioSessionId);
+
+    sp<Client> c = new Client(
+            this, pid, connId, client, audioSessionId,
+            IPCThreadState::self()->getCallingUid());
+
     LOGV("Create new client(%d) from pid %d, fd=%d, offset=%lld, length=%lld, audioSessionId=%d",
             connId, pid, fd, offset, length, audioSessionId);
     if (NO_ERROR != c->setDataSource(fd, offset, length)) {
@@ -286,7 +294,10 @@
         pid_t pid, const sp<IMediaPlayerClient> &client,
         const sp<IStreamSource> &source, int audioSessionId) {
     int32_t connId = android_atomic_inc(&mNextConnId);
-    sp<Client> c = new Client(this, pid, connId, client, audioSessionId);
+
+    sp<Client> c = new Client(
+            this, pid, connId, client, audioSessionId,
+            IPCThreadState::self()->getCallingUid());
 
     LOGV("Create new client(%d) from pid %d, audioSessionId=%d",
          connId, pid, audioSessionId);
@@ -496,8 +507,10 @@
     mClients.remove(client);
 }
 
-MediaPlayerService::Client::Client(const sp<MediaPlayerService>& service, pid_t pid,
-        int32_t connId, const sp<IMediaPlayerClient>& client, int audioSessionId)
+MediaPlayerService::Client::Client(
+        const sp<MediaPlayerService>& service, pid_t pid,
+        int32_t connId, const sp<IMediaPlayerClient>& client,
+        int audioSessionId, uid_t uid)
 {
     LOGV("Client(%d) constructor", connId);
     mPid = pid;
@@ -507,6 +520,7 @@
     mLoop = false;
     mStatus = NO_INIT;
     mAudioSessionId = audioSessionId;
+    mUID = uid;
 
 #if CALLBACK_ANTAGONIZER
     LOGD("create Antagonizer");
@@ -671,6 +685,9 @@
     if (p == NULL) {
         p = android::createPlayer(playerType, this, notify);
     }
+
+    p->setUID(mUID);
+
     return p;
 }
 
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 8bab471..e32b92a 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -306,7 +306,8 @@
                                         pid_t pid,
                                         int32_t connId,
                                         const sp<IMediaPlayerClient>& client,
-                                        int audioSessionId);
+                                        int audioSessionId,
+                                        uid_t uid);
                                 Client();
         virtual                 ~Client();
 
@@ -336,6 +337,7 @@
                     bool                        mLoop;
                     int32_t                     mConnId;
                     int                         mAudioSessionId;
+                    uid_t                       mUID;
 
         // Metadata filters.
         media::Metadata::Filter mMetadataAllow;  // protected by mLock
diff --git a/media/libmediaplayerservice/StagefrightPlayer.cpp b/media/libmediaplayerservice/StagefrightPlayer.cpp
index 870e290..40e055c 100644
--- a/media/libmediaplayerservice/StagefrightPlayer.cpp
+++ b/media/libmediaplayerservice/StagefrightPlayer.cpp
@@ -47,6 +47,12 @@
     return OK;
 }
 
+status_t StagefrightPlayer::setUID(uid_t uid) {
+    mPlayer->setUID(uid);
+
+    return OK;
+}
+
 status_t StagefrightPlayer::setDataSource(
         const char *url, const KeyedVector<String8, String8> *headers) {
     return mPlayer->setDataSource(url, headers);
diff --git a/media/libmediaplayerservice/StagefrightPlayer.h b/media/libmediaplayerservice/StagefrightPlayer.h
index 85a546d..cbc6d49 100644
--- a/media/libmediaplayerservice/StagefrightPlayer.h
+++ b/media/libmediaplayerservice/StagefrightPlayer.h
@@ -31,6 +31,8 @@
 
     virtual status_t initCheck();
 
+    virtual status_t setUID(uid_t uid);
+
     virtual status_t setDataSource(
             const char *url, const KeyedVector<String8, String8> *headers);
 
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
index b3b3af5..5a5330d 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
@@ -35,8 +35,11 @@
 
 NuPlayer::HTTPLiveSource::HTTPLiveSource(
         const char *url,
-        const KeyedVector<String8, String8> *headers)
+        const KeyedVector<String8, String8> *headers,
+        bool uidValid, uid_t uid)
     : mURL(url),
+      mUIDValid(uidValid),
+      mUID(uid),
       mFlags(0),
       mEOS(false),
       mOffset(0) {
@@ -65,7 +68,8 @@
     mLiveLooper->start();
 
     mLiveSession = new LiveSession(
-            (mFlags & kFlagIncognito) ? LiveSession::kFlagIncognito : 0);
+            (mFlags & kFlagIncognito) ? LiveSession::kFlagIncognito : 0,
+            mUIDValid, mUID);
 
     mLiveLooper->registerHandler(mLiveSession);
 
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
index 7a337e9..36c67c5 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
@@ -29,7 +29,9 @@
 struct NuPlayer::HTTPLiveSource : public NuPlayer::Source {
     HTTPLiveSource(
             const char *url,
-            const KeyedVector<String8, String8> *headers);
+            const KeyedVector<String8, String8> *headers,
+            bool uidValid = false,
+            uid_t uid = 0);
 
     virtual void start();
 
@@ -54,6 +56,8 @@
 
     AString mURL;
     KeyedVector<String8, String8> mExtraHeaders;
+    bool mUIDValid;
+    uid_t mUID;
     uint32_t mFlags;
     bool mEOS;
     off64_t mOffset;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index effa703..b06f20d 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -44,7 +44,8 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 NuPlayer::NuPlayer()
-    : mAudioEOS(false),
+    : mUIDValid(false),
+      mAudioEOS(false),
       mVideoEOS(false),
       mScanSourcesPending(false),
       mScanSourcesGeneration(0),
@@ -57,6 +58,11 @@
 NuPlayer::~NuPlayer() {
 }
 
+void NuPlayer::setUID(uid_t uid) {
+    mUIDValid = true;
+    mUID = uid;
+}
+
 void NuPlayer::setDriver(const wp<NuPlayerDriver> &driver) {
     mDriver = driver;
 }
@@ -72,7 +78,7 @@
         const char *url, const KeyedVector<String8, String8> *headers) {
     sp<AMessage> msg = new AMessage(kWhatSetDataSource, id());
 
-    msg->setObject("source", new HTTPLiveSource(url, headers));
+    msg->setObject("source", new HTTPLiveSource(url, headers, mUIDValid, mUID));
     msg->post();
 }
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index fb5b001..cf9185b 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -33,6 +33,8 @@
 struct NuPlayer : public AHandler {
     NuPlayer();
 
+    void setUID(uid_t uid);
+
     void setDriver(const wp<NuPlayerDriver> &driver);
 
     void setDataSource(const sp<IStreamSource> &source);
@@ -84,6 +86,8 @@
     };
 
     wp<NuPlayerDriver> mDriver;
+    bool mUIDValid;
+    uid_t mUID;
     sp<Source> mSource;
     sp<NativeWindowWrapper> mNativeWindow;
     sp<MediaPlayerBase::AudioSink> mAudioSink;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index e1213f4..7cd8b6c 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -55,6 +55,12 @@
     return OK;
 }
 
+status_t NuPlayerDriver::setUID(uid_t uid) {
+    mPlayer->setUID(uid);
+
+    return OK;
+}
+
 status_t NuPlayerDriver::setDataSource(
         const char *url, const KeyedVector<String8, String8> *headers) {
     CHECK_EQ((int)mState, (int)UNINITIALIZED);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
index 145fd80..1bb7ca2 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
@@ -28,6 +28,8 @@
 
     virtual status_t initCheck();
 
+    virtual status_t setUID(uid_t uid);
+
     virtual status_t setDataSource(
             const char *url, const KeyedVector<String8, String8> *headers);
 
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/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index aa7edcc..77c25d1 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -128,6 +128,9 @@
     }
 
     virtual void render(MediaBuffer *buffer) {
+        int64_t timeUs;
+        CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs));
+        native_window_set_buffers_timestamp(mNativeWindow.get(), timeUs * 1000);
         status_t err = mNativeWindow->queueBuffer(
                 mNativeWindow.get(), buffer->graphicBuffer().get());
         if (err != 0) {
@@ -180,6 +183,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 AwesomePlayer::AwesomePlayer()
     : mQueueStarted(false),
+      mUIDValid(false),
       mTimeSource(NULL),
       mVideoRendererIsPreview(false),
       mAudioPlayer(NULL),
@@ -243,6 +247,13 @@
     mListener = listener;
 }
 
+void AwesomePlayer::setUID(uid_t uid) {
+    LOGI("AwesomePlayer running on behalf of uid %d", uid);
+
+    mUID = uid;
+    mUIDValid = true;
+}
+
 status_t AwesomePlayer::setDataSource(
         const char *uri, const KeyedVector<String8, String8> *headers) {
     Mutex::Autolock autoLock(mLock);
@@ -1928,6 +1939,10 @@
                     ? HTTPBase::kFlagIncognito
                     : 0);
 
+        if (mUIDValid) {
+            mConnectingDataSource->setUID(mUID);
+        }
+
         mLock.unlock();
         status_t err = mConnectingDataSource->connect(mUri, &mUriHeaders);
         mLock.lock();
@@ -2009,6 +2024,10 @@
         mRTSPController = new ARTSPController(mLooper);
         mConnectingRTSPController = mRTSPController;
 
+        if (mUIDValid) {
+            mConnectingRTSPController->setUID(mUID);
+        }
+
         mLock.unlock();
         status_t err = mRTSPController->connect(mUri.string());
         mLock.lock();
diff --git a/media/libstagefright/HTTPBase.cpp b/media/libstagefright/HTTPBase.cpp
index c0ae29d..0d24551 100644
--- a/media/libstagefright/HTTPBase.cpp
+++ b/media/libstagefright/HTTPBase.cpp
@@ -37,7 +37,8 @@
       mTotalTransferBytes(0),
       mPrevBandwidthMeasureTimeUs(0),
       mPrevEstimatedBandWidthKbps(0),
-      mBandWidthCollectFreqMs(5000) {
+      mBandWidthCollectFreqMs(5000),
+      mUIDValid(false) {
 }
 
 // static
@@ -119,4 +120,19 @@
     return OK;
 }
 
+void HTTPBase::setUID(uid_t uid) {
+    mUIDValid = true;
+    mUID = uid;
+}
+
+bool HTTPBase::getUID(uid_t *uid) const {
+    if (!mUIDValid) {
+        return false;
+    }
+
+    *uid = mUID;
+
+    return true;
+}
+
 }  // namespace android
diff --git a/media/libstagefright/HTTPStream.cpp b/media/libstagefright/HTTPStream.cpp
index a156da6..d526ebd 100644
--- a/media/libstagefright/HTTPStream.cpp
+++ b/media/libstagefright/HTTPStream.cpp
@@ -43,6 +43,7 @@
 
 HTTPStream::HTTPStream()
     : mState(READY),
+      mUIDValid(false),
       mSocket(-1),
       mSSLContext(NULL),
       mSSL(NULL) {
@@ -57,6 +58,11 @@
     }
 }
 
+void HTTPStream::setUID(uid_t uid) {
+    mUIDValid = true;
+    mUID = uid;
+}
+
 static bool MakeSocketBlocking(int s, bool blocking) {
     // Make socket non-blocking.
     int flags = fcntl(s, F_GETFL, 0);
@@ -250,6 +256,10 @@
             continue;
         }
 
+        if (mUIDValid) {
+            RegisterSocketUser(mSocket, mUID);
+        }
+
         setReceiveTimeout(30);  // Time out reads after 30 secs by default.
 
         int s = mSocket;
@@ -596,5 +606,18 @@
     CHECK_EQ(0, setsockopt(mSocket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)));
 }
 
+// static
+void HTTPStream::RegisterSocketUser(int s, uid_t uid) {
+    // Lower bits MUST be 0.
+    static const uint64_t kTag = 0xdeadbeef00000000ll;
+
+    AString line = StringPrintf("t %d %llu %d", s, kTag, uid);
+
+    int fd = open("/proc/net/xt_qtaguid/ctrl", O_WRONLY);
+    write(fd, line.c_str(), line.size());
+    close(fd);
+    fd = -1;
+}
+
 }  // namespace android
 
diff --git a/media/libstagefright/NuHTTPDataSource.cpp b/media/libstagefright/NuHTTPDataSource.cpp
index dac2ee4..2949767 100644
--- a/media/libstagefright/NuHTTPDataSource.cpp
+++ b/media/libstagefright/NuHTTPDataSource.cpp
@@ -140,6 +140,11 @@
         return ERROR_MALFORMED;
     }
 
+    uid_t uid;
+    if (getUID(&uid)) {
+        mHTTP.setUID(uid);
+    }
+
     return connect(host, port, path, https, headers, offset);
 }
 
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/chromium_http/support.cpp b/media/libstagefright/chromium_http/support.cpp
index 967f126..f4b3668 100644
--- a/media/libstagefright/chromium_http/support.cpp
+++ b/media/libstagefright/chromium_http/support.cpp
@@ -115,31 +115,31 @@
 
     mUserAgent = ua.c_str();
 
-    net_log_ = new SfNetLog;
+    set_net_log(new SfNetLog());
 
-    host_resolver_ =
+    set_host_resolver(
         net::CreateSystemHostResolver(
                 net::HostResolver::kDefaultParallelism,
                 NULL /* resolver_proc */,
-                net_log_);
+                net_log()));
 
-    ssl_config_service_ =
-        net::SSLConfigService::CreateSystemSSLConfigService();
+    set_ssl_config_service(
+        net::SSLConfigService::CreateSystemSSLConfigService());
 
-    proxy_service_ = net::ProxyService::CreateWithoutProxyResolver(
-            new net::ProxyConfigServiceAndroid, net_log_);
+    set_proxy_service(net::ProxyService::CreateWithoutProxyResolver(
+        new net::ProxyConfigServiceAndroid, net_log()));
 
-    http_transaction_factory_ = new net::HttpCache(
-            host_resolver_,
+    set_http_transaction_factory(new net::HttpCache(
+            host_resolver(),
             new net::CertVerifier(),
-            dnsrr_resolver_,
-            dns_cert_checker_.get(),
-            proxy_service_.get(),
-            ssl_config_service_.get(),
-            net::HttpAuthHandlerFactory::CreateDefault(host_resolver_),
-            network_delegate_,
-            net_log_,
-            NULL);  // backend_factory
+            dnsrr_resolver(),
+            dns_cert_checker(),
+            proxy_service(),
+            ssl_config_service(),
+            net::HttpAuthHandlerFactory::CreateDefault(host_resolver()),
+            network_delegate(),
+            net_log(),
+            NULL));  // backend_factory
 }
 
 const std::string &SfRequestContext::GetUserAgent(const GURL &url) const {
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/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index f1a2a60..8ecc17c 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -41,8 +41,10 @@
 
 const int64_t LiveSession::kMaxPlaylistAgeUs = 15000000ll;
 
-LiveSession::LiveSession(uint32_t flags)
+LiveSession::LiveSession(uint32_t flags, bool uidValid, uid_t uid)
     : mFlags(flags),
+      mUIDValid(uidValid),
+      mUID(uid),
       mDataSource(new LiveDataSource),
       mHTTPDataSource(
               HTTPBase::Create(
@@ -58,6 +60,9 @@
       mSeekDone(false),
       mDisconnectPending(false),
       mMonitorQueueGeneration(0) {
+    if (mUIDValid) {
+        mHTTPDataSource->setUID(mUID);
+    }
 }
 
 LiveSession::~LiveSession() {
@@ -671,6 +676,10 @@
                     ? HTTPBase::kFlagIncognito
                     : 0);
 
+        if (mUIDValid) {
+            keySource->setUID(mUID);
+        }
+
         status_t err = keySource->connect(keyURI.c_str());
 
         if (err == OK) {
diff --git a/media/libstagefright/include/ARTSPController.h b/media/libstagefright/include/ARTSPController.h
index ce7ffe5..2bd5be6 100644
--- a/media/libstagefright/include/ARTSPController.h
+++ b/media/libstagefright/include/ARTSPController.h
@@ -30,6 +30,8 @@
 struct ARTSPController : public MediaExtractor {
     ARTSPController(const sp<ALooper> &looper);
 
+    void setUID(uid_t uid);
+
     status_t connect(const char *url);
     void disconnect();
 
@@ -80,6 +82,9 @@
     sp<MyHandler> mHandler;
     sp<AHandlerReflector<ARTSPController> > mReflector;
 
+    bool mUIDValid;
+    uid_t mUID;
+
     void (*mSeekDoneCb)(void *);
     void *mSeekDoneCookie;
     int64_t mLastSeekCompletedTimeUs;
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index f6df380..e069b4d 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -62,6 +62,7 @@
     ~AwesomePlayer();
 
     void setListener(const wp<MediaPlayerBase> &listener);
+    void setUID(uid_t uid);
 
     status_t setDataSource(
             const char *uri,
@@ -150,6 +151,8 @@
     TimedEventQueue mQueue;
     bool mQueueStarted;
     wp<MediaPlayerBase> mListener;
+    bool mUIDValid;
+    uid_t mUID;
 
     sp<Surface> mSurface;
     sp<ANativeWindow> mNativeWindow;
diff --git a/media/libstagefright/include/HTTPBase.h b/media/libstagefright/include/HTTPBase.h
index 3a7fbb6..2e25dd9 100644
--- a/media/libstagefright/include/HTTPBase.h
+++ b/media/libstagefright/include/HTTPBase.h
@@ -48,13 +48,15 @@
 
     virtual status_t setBandwidthStatCollectFreq(int32_t freqMs);
 
+    void setUID(uid_t uid);
+    bool getUID(uid_t *uid) const;
+
     static sp<HTTPBase> Create(uint32_t flags = 0);
 
 protected:
     void addBandwidthMeasurement(size_t numBytes, int64_t delayUs);
 
 private:
-
     struct BandwidthEntry {
         int64_t mDelayUs;
         size_t mNumBytes;
@@ -76,6 +78,8 @@
     int32_t mPrevEstimatedBandWidthKbps;
     int32_t mBandWidthCollectFreqMs;
 
+    bool mUIDValid;
+    uid_t mUID;
 
     DISALLOW_EVIL_CONSTRUCTORS(HTTPBase);
 };
diff --git a/media/libstagefright/include/HTTPStream.h b/media/libstagefright/include/HTTPStream.h
index 09e6a5f..88ba9d6 100644
--- a/media/libstagefright/include/HTTPStream.h
+++ b/media/libstagefright/include/HTTPStream.h
@@ -32,6 +32,8 @@
     HTTPStream();
     ~HTTPStream();
 
+    void setUID(uid_t uid);
+
     status_t connect(const char *server, int port = -1, bool https = false);
     status_t disconnect();
 
@@ -58,6 +60,8 @@
     // _excluding_ the termianting CRLF.
     status_t receive_line(char *line, size_t size);
 
+    static void RegisterSocketUser(int s, uid_t uid);
+
 private:
     enum State {
         READY,
@@ -67,6 +71,10 @@
 
     State mState;
     Mutex mLock;
+
+    bool mUIDValid;
+    uid_t mUID;
+
     int mSocket;
 
     KeyedVector<AString, AString> mHeaders;
diff --git a/media/libstagefright/include/LiveSession.h b/media/libstagefright/include/LiveSession.h
index 99abe64..188ef5e 100644
--- a/media/libstagefright/include/LiveSession.h
+++ b/media/libstagefright/include/LiveSession.h
@@ -35,7 +35,7 @@
         // Don't log any URLs.
         kFlagIncognito = 1,
     };
-    LiveSession(uint32_t flags = 0);
+    LiveSession(uint32_t flags = 0, bool uidValid = false, uid_t uid = 0);
 
     sp<DataSource> getDataSource();
 
@@ -77,6 +77,8 @@
     };
 
     uint32_t mFlags;
+    bool mUIDValid;
+    uid_t mUID;
 
     sp<LiveDataSource> mDataSource;
 
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/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp
index c4e0cdc..072d6b2 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTSPConnection.cpp
@@ -34,13 +34,17 @@
 #include <openssl/md5.h>
 #include <sys/socket.h>
 
+#include "HTTPStream.h"
+
 namespace android {
 
 // static
 const int64_t ARTSPConnection::kSelectTimeoutUs = 1000ll;
 
-ARTSPConnection::ARTSPConnection()
-    : mState(DISCONNECTED),
+ARTSPConnection::ARTSPConnection(bool uidValid, uid_t uid)
+    : mUIDValid(uidValid),
+      mUID(uid),
+      mState(DISCONNECTED),
       mAuthType(NONE),
       mSocket(-1),
       mConnectionID(0),
@@ -246,6 +250,10 @@
 
     mSocket = socket(AF_INET, SOCK_STREAM, 0);
 
+    if (mUIDValid) {
+        HTTPStream::RegisterSocketUser(mSocket, mUID);
+    }
+
     MakeSocketBlocking(mSocket, false);
 
     struct sockaddr_in remote;
diff --git a/media/libstagefright/rtsp/ARTSPConnection.h b/media/libstagefright/rtsp/ARTSPConnection.h
index ac2e3ae..5cb84fd 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.h
+++ b/media/libstagefright/rtsp/ARTSPConnection.h
@@ -33,7 +33,7 @@
 };
 
 struct ARTSPConnection : public AHandler {
-    ARTSPConnection();
+    ARTSPConnection(bool uidValid = false, uid_t uid = 0);
 
     void connect(const char *url, const sp<AMessage> &reply);
     void disconnect(const sp<AMessage> &reply);
@@ -74,6 +74,8 @@
 
     static const int64_t kSelectTimeoutUs;
 
+    bool mUIDValid;
+    uid_t mUID;
     State mState;
     AString mUser, mPass;
     AuthType mAuthType;
diff --git a/media/libstagefright/rtsp/ARTSPController.cpp b/media/libstagefright/rtsp/ARTSPController.cpp
index 1328d2e..2ebae7e 100644
--- a/media/libstagefright/rtsp/ARTSPController.cpp
+++ b/media/libstagefright/rtsp/ARTSPController.cpp
@@ -28,6 +28,7 @@
 ARTSPController::ARTSPController(const sp<ALooper> &looper)
     : mState(DISCONNECTED),
       mLooper(looper),
+      mUIDValid(false),
       mSeekDoneCb(NULL),
       mSeekDoneCookie(NULL),
       mLastSeekCompletedTimeUs(-1) {
@@ -40,6 +41,11 @@
     mLooper->unregisterHandler(mReflector->id());
 }
 
+void ARTSPController::setUID(uid_t uid) {
+    mUIDValid = true;
+    mUID = uid;
+}
+
 status_t ARTSPController::connect(const char *url) {
     Mutex::Autolock autoLock(mLock);
 
@@ -49,7 +55,7 @@
 
     sp<AMessage> msg = new AMessage(kWhatConnectDone, mReflector->id());
 
-    mHandler = new MyHandler(url, mLooper);
+    mHandler = new MyHandler(url, mLooper, mUIDValid, mUID);
 
     mState = CONNECTING;
 
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index d15d9c5..3188959 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -40,6 +40,8 @@
 #include <sys/socket.h>
 #include <netdb.h>
 
+#include "HTTPStream.h"
+
 // If no access units are received within 5 secs, assume that the rtp
 // stream has ended and signal end of stream.
 static int64_t kAccessUnitTimeoutUs = 5000000ll;
@@ -92,10 +94,14 @@
 }
 
 struct MyHandler : public AHandler {
-    MyHandler(const char *url, const sp<ALooper> &looper)
-        : mLooper(looper),
+    MyHandler(
+            const char *url, const sp<ALooper> &looper,
+            bool uidValid = false, uid_t uid = 0)
+        : mUIDValid(uidValid),
+          mUID(uid),
+          mLooper(looper),
           mNetLooper(new ALooper),
-          mConn(new ARTSPConnection),
+          mConn(new ARTSPConnection(mUIDValid, mUID)),
           mRTPConn(new ARTPConnection),
           mOriginalSessionURL(url),
           mSessionURL(url),
@@ -1078,6 +1084,8 @@
         List<sp<ABuffer> > mPackets;
     };
 
+    bool mUIDValid;
+    uid_t mUID;
     sp<ALooper> mLooper;
     sp<ALooper> mNetLooper;
     sp<ARTSPConnection> mConn;
@@ -1172,6 +1180,11 @@
             ARTPConnection::MakePortPair(
                     &info->mRTPSocket, &info->mRTCPSocket, &rtpPort);
 
+            if (mUIDValid) {
+                HTTPStream::RegisterSocketUser(info->mRTPSocket, mUID);
+                HTTPStream::RegisterSocketUser(info->mRTCPSocket, mUID);
+            }
+
             request.append("Transport: RTP/AVP/UDP;unicast;client_port=");
             request.append(rtpPort);
             request.append("-");
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/config.xml b/packages/SystemUI/res/values/config.xml
index 7a4ac5d..5298f2e 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -36,10 +36,6 @@
     <!-- Whether or not we show the number in the bar. -->
     <bool name="config_statusBarShowNumber">true</bool>
 
-    <!-- Whether a software navigation bar should be shown. NOTE: in the future this may be
-         autodetected from the Configuration. -->
-    <bool name="config_showNavigationBar">false</bool>
-
     <!-- How many icons may be shown at once in the system bar. Includes any
          slots that may be reused for things like IME control. -->
     <integer name="config_maxNotificationIcons">5</integer>
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/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index d8474db..4c7b0dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -246,7 +246,7 @@
         mIntruderAlertView.setClickable(true);
 
         try {
-            boolean showNav = res.getBoolean(R.bool.config_showNavigationBar);
+            boolean showNav = res.getBoolean(com.android.internal.R.bool.config_showNavigationBar);
             if (showNav) {
                 mNavigationBarView = 
                     (NavigationBarView) View.inflate(context, R.layout.navigation_bar, null);
diff --git a/packages/VpnDialogs/Android.mk b/packages/VpnDialogs/Android.mk
index 89f010a..ac84125 100644
--- a/packages/VpnDialogs/Android.mk
+++ b/packages/VpnDialogs/Android.mk
@@ -20,6 +20,8 @@
 
 LOCAL_MODULE_TAGS := optional
 
+LOCAL_CERTIFICATE := platform
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := VpnDialogs
diff --git a/packages/VpnDialogs/AndroidManifest.xml b/packages/VpnDialogs/AndroidManifest.xml
index 4e6784c..c0b0a08 100644
--- a/packages/VpnDialogs/AndroidManifest.xml
+++ b/packages/VpnDialogs/AndroidManifest.xml
@@ -1,5 +1,6 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.vpndialogs">
+        package="com.android.vpndialogs"
+        android:sharedUserId="android.uid.system">
 
     <application android:label="VpnDialogs">
         <activity android:name=".ConfirmDialog"
diff --git a/packages/VpnDialogs/res/values/strings.xml b/packages/VpnDialogs/res/values/strings.xml
index 8186e26..df6d36b 100644
--- a/packages/VpnDialogs/res/values/strings.xml
+++ b/packages/VpnDialogs/res/values/strings.xml
@@ -29,6 +29,7 @@
 
     <string name="accept">I trust this application.</string>
 
+    <string name="legacy_title">VPN is connected</string>
     <string name="configure">Configure</string>
     <string name="disconnect">Disconnect</string>
 
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
index ba3f344..c076ba0 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
@@ -64,9 +64,6 @@
             mService = IConnectivityManager.Stub.asInterface(
                     ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
 
-            PackageManager pm = getPackageManager();
-            ApplicationInfo app = pm.getApplicationInfo(mConfig.packageName, 0);
-
             View view = View.inflate(this, R.layout.manage, null);
             if (mConfig.sessionName != null) {
                 ((TextView) view.findViewById(R.id.session)).setText(mConfig.sessionName);
@@ -75,15 +72,29 @@
             mDataTransmitted = (TextView) view.findViewById(R.id.data_transmitted);
             mDataReceived = (TextView) view.findViewById(R.id.data_received);
 
-            mDialog = new AlertDialog.Builder(this)
-                    .setIcon(app.loadIcon(pm))
-                    .setTitle(app.loadLabel(pm))
-                    .setView(view)
-                    .setNeutralButton(R.string.disconnect, this)
-                    .setNegativeButton(android.R.string.cancel, this)
-                    .create();
+            if (mConfig.packageName == null) {
+                // Legacy VPN does not have a package name.
+                mDialog = new AlertDialog.Builder(this)
+                        .setIcon(android.R.drawable.ic_dialog_info)
+                        .setTitle(R.string.legacy_title)
+                        .setView(view)
+                        .setNeutralButton(R.string.disconnect, this)
+                        .setNegativeButton(android.R.string.cancel, this)
+                        .create();
+            } else {
+                PackageManager pm = getPackageManager();
+                ApplicationInfo app = pm.getApplicationInfo(mConfig.packageName, 0);
 
-            if (mConfig.configureActivity != null) {
+                mDialog = new AlertDialog.Builder(this)
+                        .setIcon(app.loadIcon(pm))
+                        .setTitle(app.loadLabel(pm))
+                        .setView(view)
+                        .setNeutralButton(R.string.disconnect, this)
+                        .setNegativeButton(android.R.string.cancel, this)
+                        .create();
+            }
+
+            if (mConfig.configureIntent != null) {
                 mDialog.setButton(DialogInterface.BUTTON_POSITIVE,
                         getText(R.string.configure), this);
             }
@@ -113,9 +124,7 @@
     public void onClick(DialogInterface dialog, int which) {
         try {
             if (which == AlertDialog.BUTTON_POSITIVE) {
-                Intent intent = new Intent();
-                intent.setClassName(mConfig.packageName, mConfig.configureActivity);
-                startActivity(intent);
+                mConfig.configureIntent.send();
             } else if (which == AlertDialog.BUTTON_NEUTRAL) {
                 mService.prepareVpn("");
             }
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/input/EventHub.cpp b/services/input/EventHub.cpp
index 95b8a57..ca2540b 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -212,8 +212,8 @@
     struct input_absinfo info;
 
     if(ioctl(device->fd, EVIOCGABS(axis), &info)) {
-        LOGW("Error reading absolute controller %d for device %s fd %d\n",
-             axis, device->identifier.name.string(), device->fd);
+        LOGW("Error reading absolute controller %d for device %s fd %d, errno=%d",
+             axis, device->identifier.name.string(), device->fd, errno);
         return -errno;
     }
 
@@ -335,6 +335,33 @@
     return AKEY_STATE_UNKNOWN;
 }
 
+status_t EventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const {
+    if (axis >= 0 && axis <= ABS_MAX) {
+        AutoMutex _l(mLock);
+
+        Device* device = getDeviceLocked(deviceId);
+        if (device != NULL) {
+            return getAbsoluteAxisValueLocked(device, axis, outValue);
+        }
+    }
+    *outValue = 0;
+    return -1;
+}
+
+status_t EventHub::getAbsoluteAxisValueLocked(Device* device, int32_t axis,
+        int32_t* outValue) const {
+    struct input_absinfo info;
+
+     if(ioctl(device->fd, EVIOCGABS(axis), &info)) {
+         LOGW("Error reading absolute controller %d for device %s fd %d, errno=%d",
+              axis, device->identifier.name.string(), device->fd, errno);
+         return -errno;
+     }
+
+     *outValue = info.value;
+     return OK;
+}
+
 bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes,
         const int32_t* keyCodes, uint8_t* outFlags) const {
     AutoMutex _l(mLock);
diff --git a/services/input/EventHub.h b/services/input/EventHub.h
index 0a34e45..695dfdf 100644
--- a/services/input/EventHub.h
+++ b/services/input/EventHub.h
@@ -186,6 +186,8 @@
     virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const = 0;
     virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const = 0;
     virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const = 0;
+    virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
+            int32_t* outValue) const = 0;
 
     /*
      * Examine key input devices for specific framework keycode support
@@ -237,6 +239,7 @@
     virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const;
     virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const;
     virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const;
+    virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const;
 
     virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes,
             const int32_t* keyCodes, uint8_t* outFlags) const;
@@ -305,6 +308,7 @@
     int32_t getScanCodeStateLocked(Device* device, int32_t scanCode) const;
     int32_t getKeyCodeStateLocked(Device* device, int32_t keyCode) const;
     int32_t getSwitchStateLocked(Device* device, int32_t sw) const;
+    int32_t getAbsoluteAxisValueLocked(Device* device, int32_t axis, int32_t* outValue) const;
     bool markSupportedKeyCodesLocked(Device* device, size_t numCodes,
             const int32_t* keyCodes, uint8_t* outFlags) const;
 
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index 85ce38a..10b9083 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -1239,8 +1239,9 @@
     const InputWindow* newHoverWindow = NULL;
 
     bool isSplit = mTouchState.split;
-    bool switchedDevice = mTouchState.deviceId != entry->deviceId
-            || mTouchState.source != entry->source;
+    bool switchedDevice = mTouchState.deviceId >= 0
+            && (mTouchState.deviceId != entry->deviceId
+                    || mTouchState.source != entry->source);
     bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE
             || maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER
             || maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT);
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 82c3af3..79218a5 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -5394,7 +5394,6 @@
 
 MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device) :
         TouchInputMapper(device), mSlotCount(0), mUsingSlotsProtocol(false) {
-    clearState();
 }
 
 MultiTouchInputMapper::~MultiTouchInputMapper() {
@@ -5404,6 +5403,24 @@
     mAccumulator.clearSlots(mSlotCount);
     mAccumulator.clearButtons();
     mButtonState = 0;
+
+    if (mUsingSlotsProtocol) {
+        // Query the driver for the current slot index and use it as the initial slot
+        // before we start reading events from the device.  It is possible that the
+        // current slot index will not be the same as it was when the first event was
+        // written into the evdev buffer, which means the input mapper could start
+        // out of sync with the initial state of the events in the evdev buffer.
+        // In the extremely unlikely case that this happens, the data from
+        // two slots will be confused until the next ABS_MT_SLOT event is received.
+        // This can cause the touch point to "jump", but at least there will be
+        // no stuck touches.
+        status_t status = getEventHub()->getAbsoluteAxisValue(getDeviceId(), ABS_MT_SLOT,
+                &mAccumulator.currentSlot);
+        if (status) {
+            LOGW("Could not retrieve current multitouch slot index.  status=%d", status);
+            mAccumulator.currentSlot = -1;
+        }
+    }
 }
 
 void MultiTouchInputMapper::reset() {
@@ -5682,6 +5699,8 @@
     }
 
     mAccumulator.allocateSlots(mSlotCount);
+
+    clearState();
 }
 
 
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index e349248..d3c5ece 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -429,6 +429,7 @@
         KeyedVector<int32_t, int32_t> keyCodeStates;
         KeyedVector<int32_t, int32_t> scanCodeStates;
         KeyedVector<int32_t, int32_t> switchStates;
+        KeyedVector<int32_t, int32_t> absoluteAxisValue;
         KeyedVector<int32_t, KeyInfo> keys;
         KeyedVector<int32_t, bool> leds;
         Vector<VirtualKeyDefinition> virtualKeys;
@@ -514,6 +515,11 @@
         device->switchStates.replaceValueFor(switchCode, state);
     }
 
+    void setAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t value) {
+        Device* device = getDevice(deviceId);
+        device->absoluteAxisValue.replaceValueFor(axis, value);
+    }
+
     void addKey(int32_t deviceId, int32_t scanCode, int32_t keyCode, uint32_t flags) {
         Device* device = getDevice(deviceId);
         KeyInfo info;
@@ -677,6 +683,20 @@
         return AKEY_STATE_UNKNOWN;
     }
 
+    virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
+            int32_t* outValue) const {
+        Device* device = getDevice(deviceId);
+        if (device) {
+            ssize_t index = device->absoluteAxisValue.indexOfKey(axis);
+            if (index >= 0) {
+                *outValue = device->absoluteAxisValue.valueAt(index);
+                return OK;
+            }
+        }
+        *outValue = 0;
+        return -1;
+    }
+
     virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
             uint8_t* outFlags) const {
         bool result = false;
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 8fb6274..663f4f4 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -2533,7 +2533,7 @@
         private VpnCallback() {
         }
 
-        public synchronized void override(String[] dnsServers) {
+        public synchronized void override(List<String> dnsServers, List<String> searchDomains) {
             // TODO: override DNS servers and http proxy.
         }
 
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index adc6570..1c150f8 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -16,10 +16,11 @@
 
 package com.android.server;
 
+import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
 import static android.net.NetworkStats.IFACE_ALL;
 import static android.net.NetworkStats.TAG_NONE;
 import static android.net.NetworkStats.UID_ALL;
-import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
+import static android.provider.Settings.Secure.NETSTATS_ENABLED;
 
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -35,6 +36,7 @@
 import android.os.INetworkManagementService;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.provider.Settings;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
@@ -123,6 +125,8 @@
     /** Set of UIDs with active reject rules. */
     private SparseBooleanArray mUidRejectOnQuota = new SparseBooleanArray();
 
+    private boolean mBandwidthControlEnabled;
+
     /**
      * Constructs a new NetworkManagementService instance
      *
@@ -161,6 +165,29 @@
         return new NetworkManagementService(context, procRoot);
     }
 
+    public void systemReady() {
+
+        // only enable bandwidth control when support exists, and requested by
+        // system setting.
+        // TODO: eventually migrate to be always enabled
+        final boolean hasKernelSupport = new File("/proc/net/xt_qtaguid/ctrl").exists();
+        final boolean shouldEnable =
+                Settings.Secure.getInt(mContext.getContentResolver(), NETSTATS_ENABLED, 0) != 0;
+
+        mBandwidthControlEnabled = false;
+        if (hasKernelSupport && shouldEnable) {
+            Slog.d(TAG, "enabling bandwidth control");
+            try {
+                mConnector.doCommand("bandwidth enable");
+                mBandwidthControlEnabled = true;
+            } catch (NativeDaemonConnectorException e) {
+                Slog.e(TAG, "problem enabling bandwidth controls", e);
+            }
+        } else {
+            Slog.d(TAG, "not enabling bandwidth control");
+        }
+    }
+
     public void registerObserver(INetworkManagementEventObserver obs) {
         Slog.d(TAG, "Registering observer");
         mObservers.add(obs);
@@ -919,7 +946,7 @@
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
 
-        if (mProcStatsNetfilter.exists()) {
+        if (mBandwidthControlEnabled) {
             return getNetworkStatsDetailNetfilter(UID_ALL);
         } else {
             return getNetworkStatsDetailUidstat(UID_ALL);
@@ -930,6 +957,10 @@
     public void setInterfaceQuota(String iface, long quota) {
         mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
 
+        // silently discard when control disabled
+        // TODO: eventually migrate to be always enabled
+        if (!mBandwidthControlEnabled) return;
+
         synchronized (mInterfaceQuota) {
             if (mInterfaceQuota.contains(iface)) {
                 // TODO: eventually consider throwing
@@ -953,6 +984,10 @@
     public void removeInterfaceQuota(String iface) {
         mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
 
+        // silently discard when control disabled
+        // TODO: eventually migrate to be always enabled
+        if (!mBandwidthControlEnabled) return;
+
         synchronized (mInterfaceQuota) {
             if (!mInterfaceQuota.contains(iface)) {
                 // TODO: eventually consider throwing
@@ -976,6 +1011,10 @@
     public void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) {
         mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
 
+        // silently discard when control disabled
+        // TODO: eventually migrate to be always enabled
+        if (!mBandwidthControlEnabled) return;
+
         synchronized (mUidRejectOnQuota) {
             final boolean oldRejectOnQuota = mUidRejectOnQuota.get(uid, false);
             if (oldRejectOnQuota == rejectOnQuotaInterfaces) {
@@ -1011,7 +1050,7 @@
                     android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
         }
 
-        if (mProcStatsNetfilter.exists()) {
+        if (mBandwidthControlEnabled) {
             return getNetworkStatsDetailNetfilter(uid);
         } else {
             return getNetworkStatsDetailUidstat(uid);
@@ -1151,12 +1190,6 @@
         return getInterfaceThrottle(iface, false);
     }
 
-    @Override
-    public void setBandwidthControlEnabled(boolean enabled) {
-        mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
-        mConnector.doCommand(String.format("bandwidth %s", (enabled ? "enable" : "disable")));
-    }
-
     /**
      * Split given line into {@link ArrayList}.
      */
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index dbfd145..8c7e279 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -523,6 +523,7 @@
         // These are needed to propagate to the runnable below.
         final Context contextF = context;
         final BatteryService batteryF = battery;
+        final NetworkManagementService networkManagementF = networkManagement;
         final NetworkStatsService networkStatsF = networkStats;
         final NetworkPolicyManagerService networkPolicyF = networkPolicy;
         final ConnectivityService connectivityF = connectivity;
@@ -550,6 +551,7 @@
 
                 startSystemUi(contextF);
                 if (batteryF != null) batteryF.systemReady();
+                if (networkManagementF != null) networkManagementF.systemReady();
                 if (networkStatsF != null) networkStatsF.systemReady();
                 if (networkPolicyF != null) networkPolicyF.systemReady();
                 if (connectivityF != null) connectivityF.systemReady();
diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java
index 54bddb2..a8be916 100644
--- a/services/java/com/android/server/connectivity/Vpn.java
+++ b/services/java/com/android/server/connectivity/Vpn.java
@@ -40,9 +40,9 @@
 import com.android.internal.net.VpnConfig;
 import com.android.server.ConnectivityService.VpnCallback;
 
-import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.charset.Charsets;
+import java.util.Arrays;
 
 /**
  * @hide
@@ -77,11 +77,13 @@
             return mPackageName;
         }
 
-        // Check the permission of the caller.
-        PackageManager pm = mContext.getPackageManager();
-        VpnConfig.enforceCallingPackage(pm.getNameForUid(Binder.getCallingUid()));
+        // Only system user can call this method.
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException("Unauthorized Caller");
+        }
 
         // Check the permission of the given package.
+        PackageManager pm = mContext.getPackageManager();
         if (packageName.isEmpty()) {
             packageName = null;
         } else if (pm.checkPermission(VPN, packageName) != PackageManager.PERMISSION_GRANTED) {
@@ -104,6 +106,12 @@
             mContext.sendBroadcast(intent);
         }
 
+        // Stop legacy VPN if it has been started.
+        if (mLegacyVpnRunner != null) {
+            mLegacyVpnRunner.exit();
+            mLegacyVpnRunner = null;
+        }
+
         Log.i(TAG, "Switched from " + mPackageName + " to " + packageName);
         mPackageName = packageName;
         return mPackageName;
@@ -132,7 +140,7 @@
     /**
      * Configure a TUN interface and return its file descriptor.
      *
-     * @param configuration The parameters to configure the interface.
+     * @param config The parameters to configure the interface.
      * @return The file descriptor of the interface.
      */
     public synchronized ParcelFileDescriptor establish(VpnConfig config) {
@@ -151,11 +159,25 @@
             return null;
         }
 
-        // Create and configure the interface.
+        // Load the label.
+        String label = app.loadLabel(pm).toString();
+
+        // Load the icon and convert it into a bitmap.
+        Drawable icon = app.loadIcon(pm);
+        Bitmap bitmap = null;
+        if (icon.getIntrinsicWidth() > 0 && icon.getIntrinsicHeight() > 0) {
+            int width = mContext.getResources().getDimensionPixelSize(
+                    android.R.dimen.notification_large_icon_width);
+            int height = mContext.getResources().getDimensionPixelSize(
+                    android.R.dimen.notification_large_icon_height);
+            icon.setBounds(0, 0, width, height);
+            bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+            icon.draw(new Canvas(bitmap));
+        }
+
+        // Create the interface and abort if any of the following steps fails.
         ParcelFileDescriptor descriptor =
                 ParcelFileDescriptor.adoptFd(jniCreateInterface(config.mtu));
-
-        // Abort if any of the following steps fails.
         try {
             String name = jniGetInterfaceName(descriptor.getFd());
             if (jniSetAddresses(name, config.addresses) < 1) {
@@ -177,12 +199,15 @@
             throw e;
         }
 
-        String dnsServers = (config.dnsServers == null) ? "" : config.dnsServers.trim();
-        mCallback.override(dnsServers.isEmpty() ? null : dnsServers.split(" "));
+        // Override DNS servers and search domains.
+        mCallback.override(config.dnsServers, config.searchDomains);
 
+        // Fill more values.
         config.packageName = mPackageName;
         config.interfaceName = mInterfaceName;
-        showNotification(pm, app, config);
+
+        // Show the notification!
+        showNotification(config, label, bitmap);
         return descriptor;
     }
 
@@ -202,41 +227,26 @@
     public synchronized void interfaceRemoved(String name) {
         if (name.equals(mInterfaceName) && jniCheckInterface(name) == 0) {
             hideNotification();
-            mInterfaceName = null;
             mCallback.restore();
+            mInterfaceName = null;
         }
     }
 
-    private void showNotification(PackageManager pm, ApplicationInfo app, VpnConfig config) {
+    private void showNotification(VpnConfig config, String label, Bitmap icon) {
         NotificationManager nm = (NotificationManager)
                 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
 
         if (nm != null) {
-            // Load the icon and convert it into a bitmap.
-            Drawable icon = app.loadIcon(pm);
-            Bitmap bitmap = null;
-            if (icon.getIntrinsicWidth() > 0 && icon.getIntrinsicHeight() > 0) {
-                int width = mContext.getResources().getDimensionPixelSize(
-                        android.R.dimen.notification_large_icon_width);
-                int height = mContext.getResources().getDimensionPixelSize(
-                        android.R.dimen.notification_large_icon_height);
-                icon.setBounds(0, 0, width, height);
-                bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-                icon.draw(new Canvas(bitmap));
-            }
-
-            // Load the label.
-            String label = app.loadLabel(pm).toString();
-
-            // Build the notification.
+            String title = (label == null) ? mContext.getString(R.string.vpn_title) :
+                    mContext.getString(R.string.vpn_title_long, label);
             String text = (config.sessionName == null) ? mContext.getString(R.string.vpn_text) :
                     mContext.getString(R.string.vpn_text_long, config.sessionName);
+
             long identity = Binder.clearCallingIdentity();
             Notification notification = new Notification.Builder(mContext)
                     .setSmallIcon(R.drawable.vpn_connected)
-                    .setLargeIcon(bitmap)
-                    .setTicker(mContext.getString(R.string.vpn_ticker, label))
-                    .setContentTitle(mContext.getString(R.string.vpn_title, label))
+                    .setLargeIcon(icon)
+                    .setContentTitle(title)
                     .setContentText(text)
                     .setContentIntent(VpnConfig.getIntentForNotification(mContext, config))
                     .setDefaults(Notification.DEFAULT_ALL)
@@ -267,27 +277,22 @@
     private native void jniProtectSocket(int fd, String name);
 
     /**
-     * Handle legacy VPN requests. This method stops the services and restart
+     * Handle legacy VPN requests. This method stops the daemons and restart
      * them if their arguments are not null. Heavy things are offloaded to
      * another thread, so callers will not be blocked too long.
      *
      * @param raoocn The arguments to be passed to racoon.
      * @param mtpd The arguments to be passed to mtpd.
      */
-    public synchronized void doLegacyVpn(String[] racoon, String[] mtpd) {
-        // Currently only system user is allowed.
-        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
-            throw new SecurityException("Unauthorized Caller");
-        }
+    public synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
+        // Stop the current VPN just like a normal VPN application.
+        prepare("");
 
-        // If the previous runner is still alive, interrupt it.
-        if (mLegacyVpnRunner != null && mLegacyVpnRunner.isAlive()) {
-            mLegacyVpnRunner.interrupt();
-        }
+        // Legacy VPN does not have a package name.
+        config.packageName = null;
 
         // Start a new runner and we are done!
-        mLegacyVpnRunner = new LegacyVpnRunner(
-                new String[] {"racoon", "mtpd"}, racoon, mtpd);
+        mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd);
         mLegacyVpnRunner.start();
     }
 
@@ -300,17 +305,25 @@
      */
     private class LegacyVpnRunner extends Thread {
         private static final String TAG = "LegacyVpnRunner";
-
         private static final String NONE = "--";
 
-        private final String[] mServices;
+        private final VpnConfig mConfig;
+        private final String[] mDaemons;
         private final String[][] mArguments;
         private long mTimer = -1;
 
-        public LegacyVpnRunner(String[] services, String[]... arguments) {
+        public LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd) {
             super(TAG);
-            mServices = services;
-            mArguments = arguments;
+            mConfig = config;
+            mDaemons = new String[] {"racoon", "mtpd"};
+            mArguments = new String[][] {racoon, mtpd};
+        }
+
+        public void exit() {
+            for (String daemon : mDaemons) {
+                SystemProperties.set("ctl.stop", daemon);
+            }
+            interrupt();
         }
 
         @Override
@@ -318,9 +331,9 @@
             // Wait for the previous thread since it has been interrupted.
             Log.v(TAG, "wait");
             synchronized (TAG) {
-                Log.v(TAG, "run");
+                Log.v(TAG, "begin");
                 execute();
-                Log.v(TAG, "exit");
+                Log.v(TAG, "end");
             }
         }
 
@@ -342,14 +355,14 @@
                 // Initialize the timer.
                 checkpoint(false);
 
-                // First stop the services.
-                for (String service : mServices) {
-                    SystemProperties.set("ctl.stop", service);
+                // First stop the daemons.
+                for (String daemon : mDaemons) {
+                    SystemProperties.set("ctl.stop", daemon);
                 }
 
-                // Wait for the services to stop.
-                for (String service : mServices) {
-                    String key = "init.svc." + service;
+                // Wait for the daemons to stop.
+                for (String daemon : mDaemons) {
+                    String key = "init.svc." + daemon;
                     while (!"stopped".equals(SystemProperties.get(key))) {
                         checkpoint(true);
                     }
@@ -363,7 +376,7 @@
                     checkpoint(true);
                 }
 
-                // Check if we need to restart some services.
+                // Check if we need to restart some daemons.
                 boolean restart = false;
                 for (String[] arguments : mArguments) {
                     restart = restart || (arguments != null);
@@ -372,19 +385,19 @@
                     return;
                 }
 
-                // Start the service with arguments.
-                for (int i = 0; i < mServices.length; ++i) {
+                // Start the daemon with arguments.
+                for (int i = 0; i < mDaemons.length; ++i) {
                     String[] arguments = mArguments[i];
                     if (arguments == null) {
                         continue;
                     }
 
-                    // Start the service.
-                    String service = mServices[i];
-                    SystemProperties.set("ctl.start", service);
+                    // Start the daemon.
+                    String daemon = mDaemons[i];
+                    SystemProperties.set("ctl.start", daemon);
 
-                    // Wait for the service to start.
-                    String key = "init.svc." + service;
+                    // Wait for the daemon to start.
+                    String key = "init.svc." + daemon;
                     while (!"running".equals(SystemProperties.get(key))) {
                         checkpoint(true);
                     }
@@ -392,7 +405,7 @@
                     // Create the control socket.
                     LocalSocket socket = new LocalSocket();
                     LocalSocketAddress address = new LocalSocketAddress(
-                            service, LocalSocketAddress.Namespace.RESERVED);
+                            daemon, LocalSocketAddress.Namespace.RESERVED);
 
                     // Wait for the socket to connect.
                     while (true) {
@@ -407,22 +420,22 @@
                     socket.setSoTimeout(500);
 
                     // Send over the arguments.
-                    OutputStream output = socket.getOutputStream();
+                    OutputStream out = socket.getOutputStream();
                     for (String argument : arguments) {
                         byte[] bytes = argument.getBytes(Charsets.UTF_8);
                         if (bytes.length >= 0xFFFF) {
                             throw new IllegalArgumentException("argument too large");
                         }
-                        output.write(bytes.length >> 8);
-                        output.write(bytes.length);
-                        output.write(bytes);
+                        out.write(bytes.length >> 8);
+                        out.write(bytes.length);
+                        out.write(bytes);
                         checkpoint(false);
                     }
 
                     // Send End-Of-Arguments.
-                    output.write(0xFF);
-                    output.write(0xFF);
-                    output.flush();
+                    out.write(0xFF);
+                    out.write(0xFF);
+                    out.flush();
                     socket.close();
                 }
 
@@ -433,25 +446,47 @@
                 while (NONE.equals(SystemProperties.get("vpn.dns")) ||
                         NONE.equals(SystemProperties.get("vpn.via"))) {
 
-                    // Check if a running service is dead.
-                    for (int i = 0; i < mServices.length; ++i) {
-                        String service = mServices[i];
+                    // Check if a running daemon is dead.
+                    for (int i = 0; i < mDaemons.length; ++i) {
+                        String daemon = mDaemons[i];
                         if (mArguments[i] != null && !"running".equals(
-                                SystemProperties.get("init.svc." + service))) {
-                            throw new IllegalArgumentException(service + " is dead");
+                                SystemProperties.get("init.svc." + daemon))) {
+                            throw new IllegalArgumentException(daemon + " is dead");
                         }
                     }
                     checkpoint(true);
                 }
 
-                // Great! Now we are connected!
-                Log.i(TAG, "connected!");
-                // TODO:
+                // Now we are connected. Get the interface.
+                mConfig.interfaceName = SystemProperties.get("vpn.via");
 
+                // Get the DNS servers if they are not set in config.
+                if (mConfig.dnsServers == null || mConfig.dnsServers.size() == 0) {
+                    String dnsServers = SystemProperties.get("vpn.dns").trim();
+                    if (!dnsServers.isEmpty()) {
+                        mConfig.dnsServers = Arrays.asList(dnsServers.split(" "));
+                    }
+                }
+
+                // TODO: support routes and search domains for IPSec Mode-CFG.
+
+                // This is it! Here is the end of our journey!
+                synchronized (Vpn.this) {
+                    // Check if the thread is interrupted while we are waiting.
+                    checkpoint(false);
+
+                    if (mConfig.routes != null) {
+                        jniSetRoutes(mConfig.interfaceName, mConfig.routes);
+                    }
+                    mInterfaceName = mConfig.interfaceName;
+                    mCallback.override(mConfig.dnsServers, mConfig.searchDomains);
+                    showNotification(mConfig, null, null);
+                }
+                Log.i(TAG, "Connected!");
             } catch (Exception e) {
-                Log.i(TAG, e.getMessage());
-                for (String service : mServices) {
-                    SystemProperties.set("ctl.stop", service);
+                Log.i(TAG, "Abort due to " + e.getMessage());
+                for (String daemon : mDaemons) {
+                    SystemProperties.set("ctl.stop", daemon);
                 }
             }
         }
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index 1f2ec2c..2a17cbe 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1044,8 +1044,11 @@
 
         // TODO: only dispatch when rules actually change
 
-        // record rule locally to dispatch to new listeners
-        mUidRules.put(uid, uidRules);
+        if (uidRules == RULE_ALLOW_ALL) {
+            mUidRules.delete(uid);
+        } else {
+            mUidRules.put(uid, uidRules);
+        }
 
         final boolean rejectMetered = (uidRules & RULE_REJECT_METERED) != 0;
         setUidNetworkRules(uid, rejectMetered);
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 7610a11..b4bd176 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -134,7 +134,6 @@
      * Settings that can be changed externally.
      */
     public interface NetworkStatsSettings {
-        public boolean getEnabled();
         public long getPollInterval();
         public long getPersistThreshold();
         public long getNetworkBucketDuration();
@@ -207,20 +206,6 @@
     }
 
     public void systemReady() {
-        if (mSettings.getEnabled()) {
-            try {
-                // enable low-level bandwidth stats and control
-                // TODO: consider shipping with this enabled by default
-                mNetworkManager.setBandwidthControlEnabled(true);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "problem talking to netd while enabling bandwidth controls", e);
-            } catch (NativeDaemonConnectorException ndce) {
-                Slog.e(TAG, "problem enabling bandwidth controls", ndce);
-            }
-        } else {
-            Slog.w(TAG, "detailed network stats disabled");
-        }
-
         synchronized (mStatsLock) {
             // read historical network stats from disk, since policy service
             // might need them right away. we delay loading detailed UID stats
@@ -389,6 +374,15 @@
         }
     }
 
+    @Override
+    public void forceUpdate() {
+        mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
+
+        synchronized (mStatsLock) {
+            performPollLocked(true, false);
+        }
+    }
+
     /**
      * Receiver that watches for {@link IConnectivityManager} to claim network
      * interfaces. Used to associate {@link TelephonyManager#getSubscriberId()}
@@ -905,6 +899,8 @@
             argSet.add(arg);
         }
 
+        final boolean fullHistory = argSet.contains("full");
+
         synchronized (mStatsLock) {
             // TODO: remove this testing code, since it corrupts stats
             if (argSet.contains("generate")) {
@@ -930,7 +926,7 @@
             for (NetworkIdentitySet ident : mNetworkStats.keySet()) {
                 final NetworkStatsHistory history = mNetworkStats.get(ident);
                 pw.print("  ident="); pw.println(ident.toString());
-                history.dump("  ", pw);
+                history.dump("  ", pw, fullHistory);
             }
 
             if (argSet.contains("detail")) {
@@ -950,7 +946,7 @@
                         final NetworkStatsHistory history = uidStats.valueAt(i);
                         pw.print("    UID="); pw.print(uid);
                         pw.print(" tag="); pw.println(tag);
-                        history.dump("    ", pw);
+                        history.dump("    ", pw, fullHistory);
                     }
                 }
             }
@@ -1058,15 +1054,6 @@
             return Settings.Secure.getLong(mResolver, name, def);
         }
 
-        public boolean getEnabled() {
-            if (!new File("/proc/net/xt_qtaguid/ctrl").exists()) {
-                Slog.w(TAG, "kernel does not support bandwidth control");
-                return false;
-            }
-            // TODO: once things stabilize, enable by default.
-            // For now: ./vendor/google/tools/override-gservices secure:netstats_enabled=1
-            return Settings.Secure.getInt(mResolver, NETSTATS_ENABLED, 0) != 0;
-        }
         public long getPollInterval() {
             return getSecureLong(NETSTATS_POLL_INTERVAL, 15 * MINUTE_IN_MILLIS);
         }
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/services/jni/com_android_server_connectivity_Vpn.cpp b/services/jni/com_android_server_connectivity_Vpn.cpp
index a0ea92b..62d7636 100644
--- a/services/jni/com_android_server_connectivity_Vpn.cpp
+++ b/services/jni/com_android_server_connectivity_Vpn.cpp
@@ -244,7 +244,7 @@
                 break;
             }
 
-            in_addr_t mask = prefix ? (~0 << (32 - prefix)) : 1;
+            in_addr_t mask = prefix ? (~0 << (32 - prefix)) : 0x80000000;
             *as_in_addr(&rt4.rt_genmask) = htonl(mask);
             if (ioctl(inet4, SIOCADDRT, &rt4) && errno != EEXIST) {
                 count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR;
@@ -394,7 +394,7 @@
     }
     count = set_routes(name, routes);
     if (count < 0) {
-        throwException(env, count, "Cannot set address");
+        throwException(env, count, "Cannot set route");
         count = -1;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index 903f2b0..f2c28bb 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -610,9 +610,6 @@
         mAlarmManager.setInexactRepeating(
                 eq(AlarmManager.ELAPSED_REALTIME), anyLong(), anyLong(), isA(PendingIntent.class));
         expectLastCall().atLeastOnce();
-
-        mNetManager.setBandwidthControlEnabled(true);
-        expectLastCall().atLeastOnce();
     }
 
     private void expectNetworkState(NetworkState... state) throws Exception {
@@ -633,7 +630,6 @@
 
     private void expectSettings(long persistThreshold, long bucketDuration, long maxHistory)
             throws Exception {
-        expect(mSettings.getEnabled()).andReturn(true).anyTimes();
         expect(mSettings.getPollInterval()).andReturn(HOUR_IN_MILLIS).anyTimes();
         expect(mSettings.getPersistThreshold()).andReturn(persistThreshold).anyTimes();
         expect(mSettings.getNetworkBucketDuration()).andReturn(bucketDuration).anyTimes();
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/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index fce7cdc..2f010e5 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -473,8 +473,8 @@
                 + " EmergOnly=" + mIsEmergencyOnly);
     }
 
-    public void setStateOutOfService() {
-        mState = STATE_OUT_OF_SERVICE;
+    private void setNullState(int state) {
+        mState = state;
         mRoaming = false;
         mOperatorAlphaLong = null;
         mOperatorAlphaShort = null;
@@ -491,23 +491,12 @@
         mIsEmergencyOnly = false;
     }
 
-    // TODO - can't this be combined with the above method?
+    public void setStateOutOfService() {
+        setNullState(STATE_OUT_OF_SERVICE);
+    }
+
     public void setStateOff() {
-        mState = STATE_POWER_OFF;
-        mRoaming = false;
-        mOperatorAlphaLong = null;
-        mOperatorAlphaShort = null;
-        mOperatorNumeric = null;
-        mIsManualNetworkSelection = false;
-        mRadioTechnology = 0;
-        mCssIndicator = false;
-        mNetworkId = -1;
-        mSystemId = -1;
-        mCdmaRoamingIndicator = -1;
-        mCdmaDefaultRoamingIndicator = -1;
-        mCdmaEriIconIndex = -1;
-        mCdmaEriIconMode = -1;
-        mIsEmergencyOnly = false;
+        setNullState(STATE_POWER_OFF);
     }
 
     public void setState(int state) {
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/DefaultPhoneNotifier.java b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
index 910905a..aa7568b 100644
--- a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
+++ b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -21,6 +21,7 @@
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
@@ -55,8 +56,13 @@
     }
 
     public void notifyServiceState(Phone sender) {
+        ServiceState ss = sender.getServiceState();
+        if (ss == null) {
+            ss = new ServiceState();
+            ss.setStateOutOfService();
+        }
         try {
-            mRegistry.notifyServiceState(sender.getServiceState());
+            mRegistry.notifyServiceState(ss);
         } catch (RemoteException ex) {
             // system process is dead
         }
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..cba98c2b 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..b635373b
--- /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;
+    }
+}
diff --git a/voip/java/com/android/server/sip/SipWakeupTimer.java b/voip/java/com/android/server/sip/SipWakeupTimer.java
index 76780c0..00d47ac 100644
--- a/voip/java/com/android/server/sip/SipWakeupTimer.java
+++ b/voip/java/com/android/server/sip/SipWakeupTimer.java
@@ -83,7 +83,7 @@
         mEventQueue = null;
     }
 
-    private synchronized boolean stopped() {
+    private boolean stopped() {
         if (mEventQueue == null) {
             Log.w(TAG, "Timer stopped");
             return true;
@@ -233,7 +233,7 @@
     }
 
     @Override
-    public void onReceive(Context context, Intent intent) {
+    public synchronized void onReceive(Context context, Intent intent) {
         // This callback is already protected by AlarmManager's wake lock.
         String action = intent.getAction();
         if (getAction().equals(action)
@@ -261,7 +261,7 @@
         }
     }
 
-    private synchronized void execute(long triggerTime) {
+    private void execute(long triggerTime) {
         if (DEBUG_TIMER) Log.d(TAG, "time's up, triggerTime = "
                 + showTime(triggerTime) + ": " + mEventQueue.size());
         if (stopped() || mEventQueue.isEmpty()) return;