Merge "Make interfaces protected so they get included in api"
diff --git a/api/current.txt b/api/current.txt
index f06df4b..89654b8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11180,6 +11180,8 @@
     method public static org.apache.http.conn.ssl.SSLSocketFactory getHttpSocketFactory(int, android.net.SSLSessionCache);
     method public static javax.net.ssl.SSLSocketFactory getInsecure(int, android.net.SSLSessionCache);
     method public java.lang.String[] getSupportedCipherSuites();
+    method public void setKeyManagers(javax.net.ssl.KeyManager[]);
+    method public void setTrustManagers(javax.net.ssl.TrustManager[]);
   }
 
   public final class SSLSessionCache {
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 906a564..9bd45d3 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -277,24 +277,24 @@
     public int compatSmallestScreenWidthDp;
 
     /**
-     * @hide
+     * @hide Do not use. Implementation not finished.
      */
-    public static final int LAYOUT_DIRECTION_UNDEFINED = -1;
+    public static final int TEXT_LAYOUT_DIRECTION_UNDEFINED_DO_NOT_USE = -1;
 
     /**
-     * @hide
+     * @hide Do not use. Implementation not finished.
      */
-    public static final int LAYOUT_DIRECTION_LTR = 0;
+    public static final int TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE = 0;
 
     /**
-     * @hide
+     * @hide Do not use. Implementation not finished.
      */
-    public static final int LAYOUT_DIRECTION_RTL = 1;
+    public static final int TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE = 1;
 
     /**
-     * @hide The layout direction associated to the current Locale
+     * @hide The text layout direction associated to the current Locale
      */
-    public int layoutDirection;
+    public int textLayoutDirection;
 
     /**
      * @hide Internal book-keeping.
@@ -322,7 +322,7 @@
         mnc = o.mnc;
         if (o.locale != null) {
             locale = (Locale) o.locale.clone();
-            layoutDirection = o.layoutDirection;
+            textLayoutDirection = o.textLayoutDirection;
         }
         userSetLocale = o.userSetLocale;
         touchscreen = o.touchscreen;
@@ -358,6 +358,11 @@
         } else {
             sb.append(" (no locale)");
         }
+        switch (textLayoutDirection) {
+            case TEXT_LAYOUT_DIRECTION_UNDEFINED_DO_NOT_USE: sb.append(" ?layoutdir"); break;
+            case TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE: sb.append(" rtl"); break;
+            default: sb.append(" layoutdir="); sb.append(textLayoutDirection); break;
+        }
         if (smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
             sb.append(" sw"); sb.append(smallestScreenWidthDp); sb.append("dp");
         } else {
@@ -450,11 +455,6 @@
             case NAVIGATIONHIDDEN_YES: sb.append("/h"); break;
             default: sb.append("/"); sb.append(navigationHidden); break;
         }
-        switch (layoutDirection) {
-            case LAYOUT_DIRECTION_UNDEFINED: sb.append(" ?layoutdir"); break;
-            case LAYOUT_DIRECTION_LTR: sb.append(" ltr"); break;
-            case LAYOUT_DIRECTION_RTL: sb.append(" rtl"); break;
-        }
         if (seq != 0) {
             sb.append(" s.");
             sb.append(seq);
@@ -483,8 +483,8 @@
         screenWidthDp = compatScreenWidthDp = SCREEN_WIDTH_DP_UNDEFINED;
         screenHeightDp = compatScreenHeightDp = SCREEN_HEIGHT_DP_UNDEFINED;
         smallestScreenWidthDp = compatSmallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
+        textLayoutDirection = TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE;
         seq = 0;
-        layoutDirection = LAYOUT_DIRECTION_LTR;
     }
 
     /** {@hide} */
@@ -519,7 +519,7 @@
             changed |= ActivityInfo.CONFIG_LOCALE;
             locale = delta.locale != null
                     ? (Locale) delta.locale.clone() : null;
-            layoutDirection = getLayoutDirectionFromLocale(locale);
+            textLayoutDirection = getLayoutDirectionFromLocale(locale);
         }
         if (delta.userSetLocale && (!userSetLocale || ((changed & ActivityInfo.CONFIG_LOCALE) != 0)))
         {
@@ -611,23 +611,25 @@
     /**
      * Return the layout direction for a given Locale
      * @param locale the Locale for which we want the layout direction. Can be null.
-     * @return the layout direction. This may be one of {@link #LAYOUT_DIRECTION_UNDEFINED},
-     * {@link #LAYOUT_DIRECTION_LTR} or {@link #LAYOUT_DIRECTION_RTL}.
+     * @return the layout direction. This may be one of:
+     * {@link #TEXT_LAYOUT_DIRECTION_UNDEFINED_DO_NOT_USE} or
+     * {@link #TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE} or
+     * {@link #TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE}.
      *
      * @hide
      */
     public static int getLayoutDirectionFromLocale(Locale locale) {
-        if (locale == null || locale.equals(Locale.ROOT)) return LAYOUT_DIRECTION_UNDEFINED;
+        if (locale == null || locale.equals(Locale.ROOT)) return TEXT_LAYOUT_DIRECTION_UNDEFINED_DO_NOT_USE;
         // Be careful: this code will need to be changed when vertical scripts will be supported
         // OR if ICU4C is updated to have the "likelySubtags" file
         switch(Character.getDirectionality(locale.getDisplayName(locale).charAt(0))) {
             case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
-                return LAYOUT_DIRECTION_LTR;
+                return TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE;
             case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
             case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
-                return LAYOUT_DIRECTION_RTL;
+                return TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE;
             default:
-                return LAYOUT_DIRECTION_UNDEFINED;
+                return TEXT_LAYOUT_DIRECTION_UNDEFINED_DO_NOT_USE;
         }
     }
 
@@ -810,7 +812,7 @@
         dest.writeInt(compatScreenWidthDp);
         dest.writeInt(compatScreenHeightDp);
         dest.writeInt(compatSmallestScreenWidthDp);
-        dest.writeInt(layoutDirection);
+        dest.writeInt(textLayoutDirection);
         dest.writeInt(seq);
     }
 
@@ -838,7 +840,7 @@
         compatScreenWidthDp = source.readInt();
         compatScreenHeightDp = source.readInt();
         compatSmallestScreenWidthDp = source.readInt();
-        layoutDirection = source.readInt();
+        textLayoutDirection = source.readInt();
         seq = source.readInt();
     }
     
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index 8e50cd5..5c4b258 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -240,7 +240,6 @@
 
     /**
      * Sets the {@link TrustManager}s to be used for connections made by this factory.
-     * @hide
      */
     public void setTrustManagers(TrustManager[] trustManager) {
         mTrustManagers = trustManager;
@@ -253,7 +252,6 @@
 
     /**
      * Sets the {@link KeyManager}s to be used for connections made by this factory.
-     * @hide
      */
     public void setKeyManagers(KeyManager[] keyManagers) {
         mKeyManagers = keyManagers;
diff --git a/core/java/android/nfc/INfcTag.aidl b/core/java/android/nfc/INfcTag.aidl
index b66035f..aa5937e 100644
--- a/core/java/android/nfc/INfcTag.aidl
+++ b/core/java/android/nfc/INfcTag.aidl
@@ -43,7 +43,6 @@
     int formatNdef(int nativeHandle, in byte[] key);
     Tag rediscover(int nativehandle);
 
-    void setIsoDepTimeout(int timeout);
-    void setFelicaTimeout(int timeout);
+    int setTimeout(int technology, int timeout);
     void resetTimeouts();
 }
diff --git a/core/java/android/nfc/tech/IsoDep.java b/core/java/android/nfc/tech/IsoDep.java
index 38b2bbd..d02086f 100644
--- a/core/java/android/nfc/tech/IsoDep.java
+++ b/core/java/android/nfc/tech/IsoDep.java
@@ -16,6 +16,7 @@
 
 package android.nfc.tech;
 
+import android.nfc.ErrorCodes;
 import android.nfc.Tag;
 import android.os.Bundle;
 import android.os.RemoteException;
@@ -90,7 +91,10 @@
      */
     public void setTimeout(int timeout) {
         try {
-            mTag.getTagService().setIsoDepTimeout(timeout);
+            int err = mTag.getTagService().setTimeout(TagTechnology.ISO_DEP, timeout);
+            if (err != ErrorCodes.SUCCESS) {
+                throw new IllegalArgumentException("The supplied timeout is not valid");
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "NFC service dead", e);
         }
diff --git a/core/java/android/nfc/tech/MifareClassic.java b/core/java/android/nfc/tech/MifareClassic.java
index c4b7718..5cafe5b 100644
--- a/core/java/android/nfc/tech/MifareClassic.java
+++ b/core/java/android/nfc/tech/MifareClassic.java
@@ -16,9 +16,11 @@
 
 package android.nfc.tech;
 
+import android.nfc.ErrorCodes;
 import android.nfc.Tag;
 import android.nfc.TagLostException;
 import android.os.RemoteException;
+import android.util.Log;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
@@ -69,6 +71,8 @@
  * require the {@link android.Manifest.permission#NFC} permission.
  */
 public final class MifareClassic extends BasicTagTechnology {
+    private static final String TAG = "NFC";
+
     /**
      * The default factory key.
      */
@@ -568,6 +572,31 @@
         return transceive(data, true);
     }
 
+    /**
+     * Set the timeout of {@link #transceive} in milliseconds.
+     * <p>The timeout only applies to MifareUltralight {@link #transceive},
+     * and is reset to a default value when {@link #close} is called.
+     * <p>Setting a longer timeout may be useful when performing
+     * transactions that require a long processing time on the tag
+     * such as key generation.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param timeout timeout value in milliseconds
+     * @hide
+     */
+    // TODO Unhide for ICS
+    public void setTimeout(int timeout) {
+        try {
+            int err = mTag.getTagService().setTimeout(TagTechnology.MIFARE_CLASSIC, timeout);
+            if (err != ErrorCodes.SUCCESS) {
+                throw new IllegalArgumentException("The supplied timeout is not valid");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "NFC service dead", e);
+        }
+    }
+
     private static void validateSector(int sector) {
         // Do not be too strict on upper bounds checking, since some cards
         // have more addressable memory than they report. For example,
diff --git a/core/java/android/nfc/tech/MifareUltralight.java b/core/java/android/nfc/tech/MifareUltralight.java
index 6c2754b..3d4cdd1 100644
--- a/core/java/android/nfc/tech/MifareUltralight.java
+++ b/core/java/android/nfc/tech/MifareUltralight.java
@@ -16,10 +16,12 @@
 
 package android.nfc.tech;
 
+import android.nfc.ErrorCodes;
 import android.nfc.Tag;
 import android.nfc.TagLostException;
 import android.os.Bundle;
 import android.os.RemoteException;
+import android.util.Log;
 
 import java.io.IOException;
 
@@ -57,6 +59,8 @@
  * require the {@link android.Manifest.permission#NFC} permission.
  */
 public final class MifareUltralight extends BasicTagTechnology {
+    private static final String TAG = "NFC";
+
     /** A MIFARE Ultralight compatible tag of unknown type */
     public static final int TYPE_UNKNOWN = -1;
     /** A MIFARE Ultralight tag */
@@ -208,6 +212,32 @@
         return transceive(data, true);
     }
 
+    /**
+     * Set the timeout of {@link #transceive} in milliseconds.
+     * <p>The timeout only applies to MifareUltralight {@link #transceive},
+     * and is reset to a default value when {@link #close} is called.
+     * <p>Setting a longer timeout may be useful when performing
+     * transactions that require a long processing time on the tag
+     * such as key generation.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param timeout timeout value in milliseconds
+     * @hide
+     */
+    // TODO Unhide for ICS
+    public void setTimeout(int timeout) {
+        try {
+            int err = mTag.getTagService().setTimeout(
+                    TagTechnology.MIFARE_ULTRALIGHT, timeout);
+            if (err != ErrorCodes.SUCCESS) {
+                throw new IllegalArgumentException("The supplied timeout is not valid");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "NFC service dead", e);
+        }
+    }
+
     private static void validatePageIndex(int pageIndex) {
         // Do not be too strict on upper bounds checking, since some cards
         // may have more addressable memory than they report.
diff --git a/core/java/android/nfc/tech/NfcA.java b/core/java/android/nfc/tech/NfcA.java
index 1843eae..08095e6 100644
--- a/core/java/android/nfc/tech/NfcA.java
+++ b/core/java/android/nfc/tech/NfcA.java
@@ -16,9 +16,11 @@
 
 package android.nfc.tech;
 
+import android.nfc.ErrorCodes;
 import android.nfc.Tag;
 import android.os.Bundle;
 import android.os.RemoteException;
+import android.util.Log;
 
 import java.io.IOException;
 
@@ -33,6 +35,8 @@
  * require the {@link android.Manifest.permission#NFC} permission.
  */
 public final class NfcA extends BasicTagTechnology {
+    private static final String TAG = "NFC";
+
     /** @hide */
     public static final String EXTRA_SAK = "sak";
     /** @hide */
@@ -112,4 +116,29 @@
     public byte[] transceive(byte[] data) throws IOException {
         return transceive(data, true);
     }
+
+    /**
+     * Set the timeout of {@link #transceive} in milliseconds.
+     * <p>The timeout only applies to NfcA {@link #transceive}, and is
+     * reset to a default value when {@link #close} is called.
+     * <p>Setting a longer timeout may be useful when performing
+     * transactions that require a long processing time on the tag
+     * such as key generation.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param timeout timeout value in milliseconds
+     * @hide
+     */
+    // TODO Unhide for ICS
+    public void setTimeout(int timeout) {
+        try {
+            int err = mTag.getTagService().setTimeout(TagTechnology.NFC_A, timeout);
+            if (err != ErrorCodes.SUCCESS) {
+                throw new IllegalArgumentException("The supplied timeout is not valid");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "NFC service dead", e);
+        }
+    }
 }
diff --git a/core/java/android/nfc/tech/NfcF.java b/core/java/android/nfc/tech/NfcF.java
index 250c9b3..85abf89 100644
--- a/core/java/android/nfc/tech/NfcF.java
+++ b/core/java/android/nfc/tech/NfcF.java
@@ -16,6 +16,7 @@
 
 package android.nfc.tech;
 
+import android.nfc.ErrorCodes;
 import android.nfc.Tag;
 import android.os.Bundle;
 import android.os.RemoteException;
@@ -131,7 +132,10 @@
     // TODO Unhide for ICS
     public void setTimeout(int timeout) {
         try {
-            mTag.getTagService().setFelicaTimeout(timeout);
+            int err = mTag.getTagService().setTimeout(TagTechnology.NFC_F, timeout);
+            if (err != ErrorCodes.SUCCESS) {
+                throw new IllegalArgumentException("The supplied timeout is not valid");
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "NFC service dead", e);
         }
diff --git a/core/java/android/webkit/HTML5VideoFullScreen.java b/core/java/android/webkit/HTML5VideoFullScreen.java
index 44229a4..4cae9d8 100644
--- a/core/java/android/webkit/HTML5VideoFullScreen.java
+++ b/core/java/android/webkit/HTML5VideoFullScreen.java
@@ -253,18 +253,19 @@
         mLayout.setVisibility(View.VISIBLE);
 
         WebChromeClient client = webView.getWebChromeClient();
-        client.onShowCustomView(mLayout, mCallback);
-        // Plugins like Flash will draw over the video so hide
-        // them while we're playing.
-        if (webView.getViewManager() != null)
-            webView.getViewManager().hideAll();
+        if (client != null) {
+            client.onShowCustomView(mLayout, mCallback);
+            // Plugins like Flash will draw over the video so hide
+            // them while we're playing.
+            if (webView.getViewManager() != null)
+                webView.getViewManager().hideAll();
 
-        mProgressView = client.getVideoLoadingProgressView();
-        if (mProgressView != null) {
-            mLayout.addView(mProgressView, layoutParams);
-            mProgressView.setVisibility(View.VISIBLE);
+            mProgressView = client.getVideoLoadingProgressView();
+            if (mProgressView != null) {
+                mLayout.addView(mProgressView, layoutParams);
+                mProgressView.setVisibility(View.VISIBLE);
+            }
         }
-
     }
 
     /**
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 95e4880..10c7390 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -5605,8 +5605,8 @@
      * Adjustable parameters. Angle is the radians on a unit circle, limited
      * to quadrant 1. Values range from 0f (horizontal) to PI/2 (vertical)
      */
-    private static final float HSLOPE_TO_START_SNAP = .1f;
-    private static final float HSLOPE_TO_BREAK_SNAP = .2f;
+    private static final float HSLOPE_TO_START_SNAP = .25f;
+    private static final float HSLOPE_TO_BREAK_SNAP = .4f;
     private static final float VSLOPE_TO_START_SNAP = 1.25f;
     private static final float VSLOPE_TO_BREAK_SNAP = .95f;
     /*
@@ -5617,6 +5617,11 @@
      */
     private static final float ANGLE_VERT = 2f;
     private static final float ANGLE_HORIZ = 0f;
+    /*
+     *  The modified moving average weight.
+     *  Formula: MAV[t]=MAV[t-1] + (P[t]-MAV[t-1])/n
+     */
+    private static final float MMA_WEIGHT_N = 5;
 
     private boolean hitFocusedPlugin(int contentX, int contentY) {
         if (DebugFlags.WEB_VIEW) {
@@ -6003,8 +6008,9 @@
                 if (deltaX == 0 && deltaY == 0) {
                     keepScrollBarsVisible = done = true;
                 } else {
-                    mAverageAngle = (mAverageAngle +
-                            calculateDragAngle(deltaX, deltaY)) / 2;
+                    mAverageAngle +=
+                        (calculateDragAngle(deltaX, deltaY) - mAverageAngle)
+                        / MMA_WEIGHT_N;
                     if (mSnapScrollMode != SNAP_NONE) {
                         if (mSnapScrollMode == SNAP_Y) {
                             // radical change means getting out of snap mode
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index bda82a3..092c2f7 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -494,6 +494,14 @@
         requestLayout();
     }
 
+    private static int max2(int[] a, int valueIfEmpty) {
+        int result = valueIfEmpty;
+        for (int i = 0, N = a.length; i < N; i++) {
+            result = Math.max(result, a[i]);
+        }
+        return result;
+    }
+
     private static int sum(float[] a) {
         int result = 0;
         for (int i = 0, length = a.length; i < length; i++) {
@@ -1409,7 +1417,7 @@
         // External entry points
 
         private int size(int[] locations) {
-            return locations[locations.length - 1] - locations[0];
+            return max2(locations, 0) - locations[0];
         }
 
         private int getMin() {
@@ -1878,21 +1886,13 @@
             return result;
         }
 
-        private static int max(int[] a, int valueIfEmpty) {
-            int result = valueIfEmpty;
-            for (int i = 0, length = a.length; i < length; i++) {
-                result = Math.max(result, a[i]);
-            }
-            return result;
-        }
-
         /*
         Create a compact array of keys or values using the supplied index.
          */
         private static <K> K[] compact(K[] a, int[] index) {
             int size = a.length;
             Class<?> componentType = a.getClass().getComponentType();
-            K[] result = (K[]) Array.newInstance(componentType, max(index, -1) + 1);
+            K[] result = (K[]) Array.newInstance(componentType, max2(index, -1) + 1);
 
             // this overwrite duplicates, retaining the last equivalent entry
             for (int i = 0; i < size; i++) {
diff --git a/core/tests/coretests/src/android/content/res/ConfigurationTest.java b/core/tests/coretests/src/android/content/res/ConfigurationTest.java
index f1f745e..54a5e4e 100644
--- a/core/tests/coretests/src/android/content/res/ConfigurationTest.java
+++ b/core/tests/coretests/src/android/content/res/ConfigurationTest.java
@@ -30,169 +30,169 @@
         args = {Locale.class}
     )
     public void testGetLayoutDirectionFromLocale() {
-        assertEquals(Configuration.LAYOUT_DIRECTION_UNDEFINED,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_UNDEFINED_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(null));
 
-        assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(Locale.ENGLISH));
-        assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(Locale.CANADA));
-        assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(Locale.CANADA_FRENCH));
-        assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(Locale.FRANCE));
-        assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(Locale.FRENCH));
-        assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(Locale.GERMAN));
-        assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(Locale.GERMANY));
-        assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(Locale.ITALIAN));
-        assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(Locale.ITALY));
-        assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(Locale.UK));
-        assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(Locale.US));
 
-        assertEquals(Configuration.LAYOUT_DIRECTION_UNDEFINED,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_UNDEFINED_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(Locale.ROOT));
 
-        assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(Locale.CHINA));
-        assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(Locale.CHINESE));
-        assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(Locale.JAPAN));
-        assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(Locale.JAPANESE));
-        assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(Locale.KOREA));
-        assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(Locale.KOREAN));
-        assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(Locale.PRC));
-        assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(Locale.SIMPLIFIED_CHINESE));
-        assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(Locale.TAIWAN));
-        assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(Locale.TRADITIONAL_CHINESE));
 
         Locale locale = new Locale("ar");
-        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "AE");
-        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "BH");
-        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "DZ");
-        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "EG");
-        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "IQ");
-        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "JO");
-        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "KW");
-        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "LB");
-        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "LY");
-        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "MA");
-        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "OM");
-        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "QA");
-        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "SA");
-        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "SD");
-        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "SY");
-        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "TN");
-        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "YE");
-        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(locale));
 
         locale = new Locale("fa");
-        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(locale));
         locale = new Locale("fa", "AF");
-        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(locale));
         locale = new Locale("fa", "IR");
-        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(locale));
 
         locale = new Locale("iw");
-        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(locale));
         locale = new Locale("iw", "IL");
-        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(locale));
         locale = new Locale("he");
-        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(locale));
         locale = new Locale("he", "IL");
-        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(locale));
 
         // The following test will not pass until we are able to take care about the scrip subtag
         // thru having the "likelySubTags" file into ICU4C
 //        locale = new Locale("pa_Arab");
-//        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+//        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
 //            Configuration.getLayoutDirectionFromLocale(locale));
 //        locale = new Locale("pa_Arab", "PK");
-//        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+//        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
 //            Configuration.getLayoutDirectionFromLocale(locale));
 
         locale = new Locale("ps");
-        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ps", "AF");
-        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
             Configuration.getLayoutDirectionFromLocale(locale));
 
         // The following test will not work as the localized display name would be "Urdu" with ICU 4.4
         // We will need ICU 4.6 to get the correct localized display name
 //        locale = new Locale("ur");
-//        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+//        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
 //            Configuration.getLayoutDirectionFromLocale(locale));
 //        locale = new Locale("ur", "IN");
-//        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+//        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
 //            Configuration.getLayoutDirectionFromLocale(locale));
 //        locale = new Locale("ur", "PK");
-//        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+//        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
 //            Configuration.getLayoutDirectionFromLocale(locale));
 
         // The following test will not pass until we are able to take care about the scrip subtag
         // thru having the "likelySubTags" file into ICU4C
 //        locale = new Locale("uz_Arab");
-//        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+//        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
 //            Configuration.getLayoutDirectionFromLocale(locale));
 //        locale = new Locale("uz_Arab", "AF");
-//        assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+//        assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
 //            Configuration.getLayoutDirectionFromLocale(locale));
     }
 }
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
index 2b31462..9294df6 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -188,6 +188,11 @@
 
     status_t setBufferCountServerLocked(int bufferCount);
 
+    // computeCurrentTransformMatrix computes the transform matrix for the
+    // current texture.  It uses mCurrentTransform and the current GraphicBuffer
+    // to compute this matrix and stores it in mCurrentTransformMatrix.
+    void computeCurrentTransformMatrix();
+
     enum { INVALID_BUFFER_SLOT = -1 };
 
     struct BufferSlot {
@@ -288,9 +293,9 @@
     // by calling setBufferCount or setBufferCountServer
     int mBufferCount;
 
-    // mRequestedBufferCount is the number of buffer slots requested by the
-    // client. The default is zero, which means the client doesn't care how
-    // many buffers there is.
+    // mClientBufferCount is the number of buffer slots requested by the client.
+    // The default is zero, which means the client doesn't care how many buffers
+    // there is.
     int mClientBufferCount;
 
     // mServerBufferCount buffer count requested by the server-side
@@ -322,6 +327,11 @@
     // gets set to mLastQueuedTransform each time updateTexImage is called.
     uint32_t mCurrentTransform;
 
+    // mCurrentTransformMatrix is the transform matrix for the current texture.
+    // It gets computed by computeTransformMatrix each time updateTexImage is
+    // called.
+    float mCurrentTransformMatrix[16];
+
     // mCurrentTimestamp is the timestamp for the current texture. It
     // gets set to mLastQueuedTimestamp each time updateTexImage is called.
     int64_t mCurrentTimestamp;
@@ -362,6 +372,7 @@
     // variables of SurfaceTexture objects. It must be locked whenever the
     // member variables are accessed.
     mutable Mutex mMutex;
+
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/utils/BlobCache.h b/include/utils/BlobCache.h
new file mode 100644
index 0000000..8f76d72
--- /dev/null
+++ b/include/utils/BlobCache.h
@@ -0,0 +1,181 @@
+/*
+ ** Copyright 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 ANDROID_BLOB_CACHE_H
+#define ANDROID_BLOB_CACHE_H
+
+#include <stddef.h>
+
+#include <utils/RefBase.h>
+#include <utils/SortedVector.h>
+#include <utils/threads.h>
+
+namespace android {
+
+// A BlobCache is an in-memory cache for binary key/value pairs. All the public
+// methods are thread-safe.
+//
+// The cache contents can be serialized to a file and reloaded in a subsequent
+// execution of the program. This serialization is non-portable and should only
+// be loaded by the device that generated it.
+class BlobCache : public RefBase {
+public:
+
+    // Create an empty blob cache. The blob cache will cache key/value pairs
+    // with key and value sizes less than or equal to maxKeySize and
+    // maxValueSize, respectively. The total combined size of ALL cache entries
+    // (key sizes plus value sizes) will not exceed maxTotalSize.
+    BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize);
+
+    // set inserts a new binary value into the cache and associates it with the
+    // given binary key.  If the key or value are too large for the cache then
+    // the cache remains unchanged.  This includes the case where a different
+    // value was previously associated with the given key - the old value will
+    // remain in the cache.  If the given key and value are small enough to be
+    // put in the cache (based on the maxKeySize, maxValueSize, and maxTotalSize
+    // values specified to the BlobCache constructor), then the key/value pair
+    // will be in the cache after set returns.  Note, however, that a subsequent
+    // call to set may evict old key/value pairs from the cache.
+    //
+    // Preconditions:
+    //   key != NULL
+    //   0 < keySize
+    //   value != NULL
+    //   0 < valueSize
+    void set(const void* key, size_t keySize, const void* value,
+            size_t valueSize);
+
+    // The get function retrieves from the cache the binary value associated
+    // with a given binary key.  If the key is present in the cache then the
+    // length of the binary value associated with that key is returned.  If the
+    // value argument is non-NULL and the size of the cached value is less than
+    // valueSize bytes then the cached value is copied into the buffer pointed
+    // to by the value argument.  If the key is not present in the cache then 0
+    // is returned and the buffer pointed to by the value argument is not
+    // modified.
+    //
+    // Note that when calling get multiple times with the same key, the later
+    // calls may fail, returning 0, even if earlier calls succeeded.  The return
+    // value must be checked for each call.
+    //
+    // Preconditions:
+    //   key != NULL
+    //   0 < keySize
+    //   0 <= valueSize
+    size_t get(const void* key, size_t keySize, void* value, size_t valueSize);
+
+private:
+    // Copying is disallowed.
+    BlobCache(const BlobCache&);
+    void operator=(const BlobCache&);
+
+    // clean evicts a randomly chosen set of entries from the cache such that
+    // the total size of all remaining entries is less than mMaxTotalSize/2.
+    void clean();
+
+    // isCleanable returns true if the cache is full enough for the clean method
+    // to have some effect, and false otherwise.
+    bool isCleanable() const;
+
+    // A Blob is an immutable sized unstructured data blob.
+    class Blob : public RefBase {
+    public:
+        Blob(const void* data, size_t size, bool copyData);
+        ~Blob();
+
+        bool operator<(const Blob& rhs) const;
+
+        const void* getData() const;
+        size_t getSize() const;
+
+    private:
+        // Copying is not allowed.
+        Blob(const Blob&);
+        void operator=(const Blob&);
+
+        // mData points to the buffer containing the blob data.
+        const void* mData;
+
+        // mSize is the size of the blob data in bytes.
+        size_t mSize;
+
+        // mOwnsData indicates whether or not this Blob object should free the
+        // memory pointed to by mData when the Blob gets destructed.
+        bool mOwnsData;
+    };
+
+    // A CacheEntry is a single key/value pair in the cache.
+    class CacheEntry {
+    public:
+        CacheEntry();
+        CacheEntry(const sp<Blob>& key, const sp<Blob>& value);
+        CacheEntry(const CacheEntry& ce);
+
+        bool operator<(const CacheEntry& rhs) const;
+        const CacheEntry& operator=(const CacheEntry&);
+
+        sp<Blob> getKey() const;
+        sp<Blob> getValue() const;
+
+        void setValue(const sp<Blob>& value);
+
+    private:
+
+        // mKey is the key that identifies the cache entry.
+        sp<Blob> mKey;
+
+        // mValue is the cached data associated with the key.
+        sp<Blob> mValue;
+    };
+
+    // mMaxKeySize is the maximum key size that will be cached. Calls to
+    // BlobCache::set with a keySize parameter larger than mMaxKeySize will
+    // simply not add the key/value pair to the cache.
+    const size_t mMaxKeySize;
+
+    // mMaxValueSize is the maximum value size that will be cached. Calls to
+    // BlobCache::set with a valueSize parameter larger than mMaxValueSize will
+    // simply not add the key/value pair to the cache.
+    const size_t mMaxValueSize;
+
+    // mMaxTotalSize is the maximum size that all cache entries can occupy. This
+    // includes space for both keys and values. When a call to BlobCache::set
+    // would otherwise cause this limit to be exceeded, either the key/value
+    // pair passed to BlobCache::set will not be cached or other cache entries
+    // will be evicted from the cache to make room for the new entry.
+    const size_t mMaxTotalSize;
+
+    // mTotalSize is the total combined size of all keys and values currently in
+    // the cache.
+    size_t mTotalSize;
+
+    // mRandState is the pseudo-random number generator state. It is passed to
+    // nrand48 to generate random numbers when needed. It must be protected by
+    // mMutex.
+    unsigned short mRandState[3];
+
+    // mCacheEntries stores all the cache entries that are resident in memory.
+    // Cache entries are added to it by the 'set' method.
+    SortedVector<CacheEntry> mCacheEntries;
+
+    // mMutex is used to synchronize access to all member variables.  It must be
+    // locked any time the member variables are written or read.
+    Mutex mMutex;
+};
+
+}
+
+#endif // ANDROID_BLOB_CACHE_H
diff --git a/include/utils/threads.h b/include/utils/threads.h
index 41e5766..0bd69cf 100644
--- a/include/utils/threads.h
+++ b/include/utils/threads.h
@@ -526,6 +526,7 @@
     Thread& operator=(const Thread&);
     static  int             _threadLoop(void* user);
     const   bool            mCanCallJava;
+    // always hold mLock when reading or writing
             thread_id_t     mThread;
     mutable Mutex           mLock;
             Condition       mThreadExitedCondition;
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index ee97dcf..2cda4c8 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -96,6 +96,7 @@
     sp<ISurfaceComposer> composer(ComposerService::getComposerService());
     mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
     mNextCrop.makeInvalid();
+    memcpy(mCurrentTransformMatrix, mtxIdentity, sizeof(mCurrentTransformMatrix));
 }
 
 SurfaceTexture::~SurfaceTexture() {
@@ -547,6 +548,7 @@
         mCurrentCrop = mSlots[buf].mCrop;
         mCurrentTransform = mSlots[buf].mTransform;
         mCurrentTimestamp = mSlots[buf].mTimestamp;
+        computeCurrentTransformMatrix();
         mDequeueCondition.signal();
     } else {
         // We always bind the texture even if we don't update its contents.
@@ -596,8 +598,12 @@
 }
 
 void SurfaceTexture::getTransformMatrix(float mtx[16]) {
-    LOGV("SurfaceTexture::getTransformMatrix");
     Mutex::Autolock lock(mMutex);
+    memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
+}
+
+void SurfaceTexture::computeCurrentTransformMatrix() {
+    LOGV("SurfaceTexture::computeCurrentTransformMatrix");
 
     float xform[16];
     for (int i = 0; i < 16; i++) {
@@ -684,7 +690,7 @@
     // coordinate of 0, so SurfaceTexture must behave the same way.  We don't
     // want to expose this to applications, however, so we must add an
     // additional vertical flip to the transform after all the other transforms.
-    mtxMul(mtx, mtxFlipV, mtxBeforeFlipV);
+    mtxMul(mCurrentTransformMatrix, mtxFlipV, mtxBeforeFlipV);
 }
 
 nsecs_t SurfaceTexture::getTimestamp() {
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index 2f704c8..da04b4a 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -514,4 +514,112 @@
     thread->requestExitAndWait();
 }
 
+TEST_F(SurfaceTextureClientTest, GetTransformMatrixReturnsVerticalFlip) {
+    sp<ANativeWindow> anw(mSTC);
+    sp<SurfaceTexture> st(mST);
+    android_native_buffer_t* buf[3];
+    float mtx[16] = {};
+    ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+    ASSERT_EQ(OK, st->updateTexImage());
+    st->getTransformMatrix(mtx);
+
+    EXPECT_EQ(1.f, mtx[0]);
+    EXPECT_EQ(0.f, mtx[1]);
+    EXPECT_EQ(0.f, mtx[2]);
+    EXPECT_EQ(0.f, mtx[3]);
+
+    EXPECT_EQ(0.f, mtx[4]);
+    EXPECT_EQ(-1.f, mtx[5]);
+    EXPECT_EQ(0.f, mtx[6]);
+    EXPECT_EQ(0.f, mtx[7]);
+
+    EXPECT_EQ(0.f, mtx[8]);
+    EXPECT_EQ(0.f, mtx[9]);
+    EXPECT_EQ(1.f, mtx[10]);
+    EXPECT_EQ(0.f, mtx[11]);
+
+    EXPECT_EQ(0.f, mtx[12]);
+    EXPECT_EQ(1.f, mtx[13]);
+    EXPECT_EQ(0.f, mtx[14]);
+    EXPECT_EQ(1.f, mtx[15]);
 }
+
+TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffers) {
+    sp<ANativeWindow> anw(mSTC);
+    sp<SurfaceTexture> st(mST);
+    android_native_buffer_t* buf[3];
+    float mtx[16] = {};
+    ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+    ASSERT_EQ(OK, st->updateTexImage());
+    ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 6)); // frees buffers
+    st->getTransformMatrix(mtx);
+
+    EXPECT_EQ(1.f, mtx[0]);
+    EXPECT_EQ(0.f, mtx[1]);
+    EXPECT_EQ(0.f, mtx[2]);
+    EXPECT_EQ(0.f, mtx[3]);
+
+    EXPECT_EQ(0.f, mtx[4]);
+    EXPECT_EQ(-1.f, mtx[5]);
+    EXPECT_EQ(0.f, mtx[6]);
+    EXPECT_EQ(0.f, mtx[7]);
+
+    EXPECT_EQ(0.f, mtx[8]);
+    EXPECT_EQ(0.f, mtx[9]);
+    EXPECT_EQ(1.f, mtx[10]);
+    EXPECT_EQ(0.f, mtx[11]);
+
+    EXPECT_EQ(0.f, mtx[12]);
+    EXPECT_EQ(1.f, mtx[13]);
+    EXPECT_EQ(0.f, mtx[14]);
+    EXPECT_EQ(1.f, mtx[15]);
+}
+
+TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffersWithCrop) {
+    sp<ANativeWindow> anw(mSTC);
+    sp<SurfaceTexture> st(mST);
+    android_native_buffer_t* buf[3];
+    float mtx[16] = {};
+    android_native_rect_t crop;
+    crop.left = 0;
+    crop.top = 0;
+    crop.right = 5;
+    crop.bottom = 5;
+
+    ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
+    ASSERT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 8, 8, 0));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+    ASSERT_EQ(OK, native_window_set_crop(anw.get(), &crop));
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+    ASSERT_EQ(OK, st->updateTexImage());
+    ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 6)); // frees buffers
+    st->getTransformMatrix(mtx);
+
+    // This accounts for the 1 texel shrink for each edge that's included in the
+    // transform matrix to avoid texturing outside the crop region.
+    EXPECT_EQ(.5f, mtx[0]);
+    EXPECT_EQ(0.f, mtx[1]);
+    EXPECT_EQ(0.f, mtx[2]);
+    EXPECT_EQ(0.f, mtx[3]);
+
+    EXPECT_EQ(0.f, mtx[4]);
+    EXPECT_EQ(-.5f, mtx[5]);
+    EXPECT_EQ(0.f, mtx[6]);
+    EXPECT_EQ(0.f, mtx[7]);
+
+    EXPECT_EQ(0.f, mtx[8]);
+    EXPECT_EQ(0.f, mtx[9]);
+    EXPECT_EQ(1.f, mtx[10]);
+    EXPECT_EQ(0.f, mtx[11]);
+
+    EXPECT_EQ(0.f, mtx[12]);
+    EXPECT_EQ(.5f, mtx[13]);
+    EXPECT_EQ(0.f, mtx[14]);
+    EXPECT_EQ(1.f, mtx[15]);
+}
+
+} // namespace android
diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp
index 8747ba5..16280d2 100644
--- a/libs/gui/tests/SurfaceTexture_test.cpp
+++ b/libs/gui/tests/SurfaceTexture_test.cpp
@@ -14,11 +14,14 @@
  * limitations under the License.
  */
 
+//#define LOG_NDEBUG 0
+
 #include <gtest/gtest.h>
 #include <gui/SurfaceTexture.h>
 #include <gui/SurfaceTextureClient.h>
 #include <ui/GraphicBuffer.h>
 #include <utils/String8.h>
+#include <utils/threads.h>
 
 #include <surfaceflinger/ISurfaceComposer.h>
 #include <surfaceflinger/Surface.h>
@@ -618,4 +621,269 @@
     }
 }
 
+/*
+ * This test is for testing GL -> GL texture streaming via SurfaceTexture.  It
+ * contains functionality to create a producer thread that will perform GL
+ * rendering to an ANativeWindow that feeds frames to a SurfaceTexture.
+ * Additionally it supports interlocking the producer and consumer threads so
+ * that a specific sequence of calls can be deterministically created by the
+ * test.
+ *
+ * The intended usage is as follows:
+ *
+ * TEST_F(...) {
+ *     class PT : public ProducerThread {
+ *         virtual void render() {
+ *             ...
+ *             swapBuffers();
+ *         }
+ *     };
+ *
+ *     runProducerThread(new PT());
+ *
+ *     // The order of these calls will vary from test to test and may include
+ *     // multiple frames and additional operations (e.g. GL rendering from the
+ *     // texture).
+ *     fc->waitForFrame();
+ *     mST->updateTexImage();
+ *     fc->finishFrame();
+ * }
+ *
+ */
+class SurfaceTextureGLToGLTest : public SurfaceTextureGLTest {
+protected:
+
+    // ProducerThread is an abstract base class to simplify the creation of
+    // OpenGL ES frame producer threads.
+    class ProducerThread : public Thread {
+    public:
+        virtual ~ProducerThread() {
+        }
+
+        void setEglObjects(EGLDisplay producerEglDisplay,
+                EGLSurface producerEglSurface,
+                EGLContext producerEglContext) {
+            mProducerEglDisplay = producerEglDisplay;
+            mProducerEglSurface = producerEglSurface;
+            mProducerEglContext = producerEglContext;
+        }
+
+        virtual bool threadLoop() {
+            eglMakeCurrent(mProducerEglDisplay, mProducerEglSurface,
+                    mProducerEglSurface, mProducerEglContext);
+            render();
+            eglMakeCurrent(mProducerEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
+                    EGL_NO_CONTEXT);
+            return false;
+        }
+
+    protected:
+        virtual void render() = 0;
+
+        void swapBuffers() {
+            eglSwapBuffers(mProducerEglDisplay, mProducerEglSurface);
+        }
+
+        EGLDisplay mProducerEglDisplay;
+        EGLSurface mProducerEglSurface;
+        EGLContext mProducerEglContext;
+    };
+
+    // FrameCondition is a utility class for interlocking between the producer
+    // and consumer threads.  The FrameCondition object should be created and
+    // destroyed in the consumer thread only.  The consumer thread should set
+    // the FrameCondition as the FrameAvailableListener of the SurfaceTexture,
+    // and should call both waitForFrame and finishFrame once for each expected
+    // frame.
+    //
+    // This interlocking relies on the fact that onFrameAvailable gets called
+    // synchronously from SurfaceTexture::queueBuffer.
+    class FrameCondition : public SurfaceTexture::FrameAvailableListener {
+    public:
+        // waitForFrame waits for the next frame to arrive.  This should be
+        // called from the consumer thread once for every frame expected by the
+        // test.
+        void waitForFrame() {
+            LOGV("+waitForFrame");
+            Mutex::Autolock lock(mMutex);
+            status_t result = mFrameAvailableCondition.wait(mMutex);
+            LOGV("-waitForFrame");
+        }
+
+        // Allow the producer to return from its swapBuffers call and continue
+        // on to produce the next frame.  This should be called by the consumer
+        // thread once for every frame expected by the test.
+        void finishFrame() {
+            LOGV("+finishFrame");
+            Mutex::Autolock lock(mMutex);
+            mFrameFinishCondition.signal();
+            LOGV("-finishFrame");
+        }
+
+        // This should be called by SurfaceTexture on the producer thread.
+        virtual void onFrameAvailable() {
+            LOGV("+onFrameAvailable");
+            Mutex::Autolock lock(mMutex);
+            mFrameAvailableCondition.signal();
+            mFrameFinishCondition.wait(mMutex);
+            LOGV("-onFrameAvailable");
+        }
+
+    protected:
+        Mutex mMutex;
+        Condition mFrameAvailableCondition;
+        Condition mFrameFinishCondition;
+    };
+
+    SurfaceTextureGLToGLTest():
+            mProducerEglSurface(EGL_NO_SURFACE),
+            mProducerEglContext(EGL_NO_CONTEXT) {
+    }
+
+    virtual void SetUp() {
+        SurfaceTextureGLTest::SetUp();
+
+        EGLConfig myConfig = {0};
+        EGLint numConfigs = 0;
+        EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &myConfig,
+                1, &numConfigs));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+        mProducerEglSurface = eglCreateWindowSurface(mEglDisplay, myConfig,
+                mANW.get(), NULL);
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        ASSERT_NE(EGL_NO_SURFACE, mProducerEglSurface);
+
+        mProducerEglContext = eglCreateContext(mEglDisplay, myConfig,
+                EGL_NO_CONTEXT, getContextAttribs());
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        ASSERT_NE(EGL_NO_CONTEXT, mProducerEglContext);
+
+        mFC = new FrameCondition();
+        mST->setFrameAvailableListener(mFC);
+    }
+
+    virtual void TearDown() {
+        if (mProducerThread != NULL) {
+            mProducerThread->requestExitAndWait();
+        }
+        if (mProducerEglContext != EGL_NO_CONTEXT) {
+            eglDestroyContext(mEglDisplay, mProducerEglContext);
+        }
+        if (mProducerEglSurface != EGL_NO_SURFACE) {
+            eglDestroySurface(mEglDisplay, mProducerEglSurface);
+        }
+        mProducerThread.clear();
+        mFC.clear();
+    }
+
+    void runProducerThread(const sp<ProducerThread> producerThread) {
+        ASSERT_TRUE(mProducerThread == NULL);
+        mProducerThread = producerThread;
+        producerThread->setEglObjects(mEglDisplay, mProducerEglSurface,
+                mProducerEglContext);
+        producerThread->run();
+    }
+
+    EGLSurface mProducerEglSurface;
+    EGLContext mProducerEglContext;
+    sp<ProducerThread> mProducerThread;
+    sp<FrameCondition> mFC;
+};
+
+// XXX: This test is disabled because it causes hangs on some devices.
+TEST_F(SurfaceTextureGLToGLTest, DISABLED_UpdateTexImageBeforeFrameFinishedWorks) {
+    class PT : public ProducerThread {
+        virtual void render() {
+            glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+            glClear(GL_COLOR_BUFFER_BIT);
+            swapBuffers();
+        }
+    };
+
+    runProducerThread(new PT());
+
+    mFC->waitForFrame();
+    mST->updateTexImage();
+    mFC->finishFrame();
+
+    // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
 }
+
+TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageAfterFrameFinishedWorks) {
+    class PT : public ProducerThread {
+        virtual void render() {
+            glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+            glClear(GL_COLOR_BUFFER_BIT);
+            swapBuffers();
+        }
+    };
+
+    runProducerThread(new PT());
+
+    mFC->waitForFrame();
+    mFC->finishFrame();
+    mST->updateTexImage();
+
+    // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
+}
+
+// XXX: This test is disabled because it causes hangs on some devices.
+TEST_F(SurfaceTextureGLToGLTest, DISABLED_RepeatedUpdateTexImageBeforeFrameFinishedWorks) {
+    enum { NUM_ITERATIONS = 1024 };
+
+    class PT : public ProducerThread {
+        virtual void render() {
+            for (int i = 0; i < NUM_ITERATIONS; i++) {
+                glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+                glClear(GL_COLOR_BUFFER_BIT);
+                LOGV("+swapBuffers");
+                swapBuffers();
+                LOGV("-swapBuffers");
+            }
+        }
+    };
+
+    runProducerThread(new PT());
+
+    for (int i = 0; i < NUM_ITERATIONS; i++) {
+        mFC->waitForFrame();
+        LOGV("+updateTexImage");
+        mST->updateTexImage();
+        LOGV("-updateTexImage");
+        mFC->finishFrame();
+
+        // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
+    }
+}
+
+// XXX: This test is disabled because it causes hangs on some devices.
+TEST_F(SurfaceTextureGLToGLTest, DISABLED_RepeatedUpdateTexImageAfterFrameFinishedWorks) {
+    enum { NUM_ITERATIONS = 1024 };
+
+    class PT : public ProducerThread {
+        virtual void render() {
+            for (int i = 0; i < NUM_ITERATIONS; i++) {
+                glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+                glClear(GL_COLOR_BUFFER_BIT);
+                LOGV("+swapBuffers");
+                swapBuffers();
+                LOGV("-swapBuffers");
+            }
+        }
+    };
+
+    runProducerThread(new PT());
+
+    for (int i = 0; i < NUM_ITERATIONS; i++) {
+        mFC->waitForFrame();
+        mFC->finishFrame();
+        LOGV("+updateTexImage");
+        mST->updateTexImage();
+        LOGV("-updateTexImage");
+
+        // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
+    }
+}
+
+} // namespace android
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk
index e8d40ba..093189c 100644
--- a/libs/utils/Android.mk
+++ b/libs/utils/Android.mk
@@ -21,6 +21,7 @@
 	Asset.cpp \
 	AssetDir.cpp \
 	AssetManager.cpp \
+	BlobCache.cpp \
 	BufferedTextOutput.cpp \
 	CallStack.cpp \
 	Debug.cpp \
diff --git a/libs/utils/BlobCache.cpp b/libs/utils/BlobCache.cpp
new file mode 100644
index 0000000..1298fa7
--- /dev/null
+++ b/libs/utils/BlobCache.cpp
@@ -0,0 +1,232 @@
+/*
+ ** Copyright 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.
+ */
+
+#define LOG_TAG "BlobCache"
+//#define LOG_NDEBUG 0
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <utils/BlobCache.h>
+#include <utils/Log.h>
+
+namespace android {
+
+BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize):
+        mMaxKeySize(maxKeySize),
+        mMaxValueSize(maxValueSize),
+        mMaxTotalSize(maxTotalSize),
+        mTotalSize(0) {
+    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+    mRandState[0] = (now >> 0) & 0xFFFF;
+    mRandState[1] = (now >> 16) & 0xFFFF;
+    mRandState[2] = (now >> 32) & 0xFFFF;
+    LOGV("initializing random seed using %lld", now);
+}
+
+void BlobCache::set(const void* key, size_t keySize, const void* value,
+        size_t valueSize) {
+    if (mMaxKeySize < keySize) {
+        LOGV("set: not caching because the key is too large: %d (limit: %d)",
+                keySize, mMaxKeySize);
+        return;
+    }
+    if (mMaxValueSize < valueSize) {
+        LOGV("set: not caching because the value is too large: %d (limit: %d)",
+                valueSize, mMaxValueSize);
+        return;
+    }
+    if (mMaxTotalSize < keySize + valueSize) {
+        LOGV("set: not caching because the combined key/value size is too "
+                "large: %d (limit: %d)", keySize + valueSize, mMaxTotalSize);
+        return;
+    }
+    if (keySize == 0) {
+        LOGW("set: not caching because keySize is 0");
+        return;
+    }
+    if (valueSize <= 0) {
+        LOGW("set: not caching because valueSize is 0");
+        return;
+    }
+
+    Mutex::Autolock lock(mMutex);
+    sp<Blob> dummyKey(new Blob(key, keySize, false));
+    CacheEntry dummyEntry(dummyKey, NULL);
+
+    while (true) {
+
+        ssize_t index = mCacheEntries.indexOf(dummyEntry);
+        if (index < 0) {
+            // Create a new cache entry.
+            sp<Blob> keyBlob(new Blob(key, keySize, true));
+            sp<Blob> valueBlob(new Blob(value, valueSize, true));
+            size_t newTotalSize = mTotalSize + keySize + valueSize;
+            if (mMaxTotalSize < newTotalSize) {
+                if (isCleanable()) {
+                    // Clean the cache and try again.
+                    clean();
+                    continue;
+                } else {
+                    LOGV("set: not caching new key/value pair because the "
+                            "total cache size limit would be exceeded: %d "
+                            "(limit: %d)",
+                            keySize + valueSize, mMaxTotalSize);
+                    break;
+                }
+            }
+            mCacheEntries.add(CacheEntry(keyBlob, valueBlob));
+            mTotalSize = newTotalSize;
+            LOGV("set: created new cache entry with %d byte key and %d byte value",
+                    keySize, valueSize);
+        } else {
+            // Update the existing cache entry.
+            sp<Blob> valueBlob(new Blob(value, valueSize, true));
+            sp<Blob> oldValueBlob(mCacheEntries[index].getValue());
+            size_t newTotalSize = mTotalSize + valueSize - oldValueBlob->getSize();
+            if (mMaxTotalSize < newTotalSize) {
+                if (isCleanable()) {
+                    // Clean the cache and try again.
+                    clean();
+                    continue;
+                } else {
+                    LOGV("set: not caching new value because the total cache "
+                            "size limit would be exceeded: %d (limit: %d)",
+                            keySize + valueSize, mMaxTotalSize);
+                    break;
+                }
+            }
+            mCacheEntries.editItemAt(index).setValue(valueBlob);
+            mTotalSize = newTotalSize;
+            LOGV("set: updated existing cache entry with %d byte key and %d byte "
+                    "value", keySize, valueSize);
+        }
+        break;
+    }
+}
+
+size_t BlobCache::get(const void* key, size_t keySize, void* value,
+        size_t valueSize) {
+    if (mMaxKeySize < keySize) {
+        LOGV("get: not searching because the key is too large: %d (limit %d)",
+                keySize, mMaxKeySize);
+        return 0;
+    }
+    Mutex::Autolock lock(mMutex);
+    sp<Blob> dummyKey(new Blob(key, keySize, false));
+    CacheEntry dummyEntry(dummyKey, NULL);
+    ssize_t index = mCacheEntries.indexOf(dummyEntry);
+    if (index < 0) {
+        LOGV("get: no cache entry found for key of size %d", keySize);
+        return 0;
+    }
+
+    // The key was found. Return the value if the caller's buffer is large
+    // enough.
+    sp<Blob> valueBlob(mCacheEntries[index].getValue());
+    size_t valueBlobSize = valueBlob->getSize();
+    if (valueBlobSize <= valueSize) {
+        LOGV("get: copying %d bytes to caller's buffer", valueBlobSize);
+        memcpy(value, valueBlob->getData(), valueBlobSize);
+    } else {
+        LOGV("get: caller's buffer is too small for value: %d (needs %d)",
+                valueSize, valueBlobSize);
+    }
+    return valueBlobSize;
+}
+
+void BlobCache::clean() {
+    // Remove a random cache entry until the total cache size gets below half
+    // the maximum total cache size.
+    while (mTotalSize > mMaxTotalSize / 2) {
+        size_t i = size_t(nrand48(mRandState) % (mCacheEntries.size()));
+        const CacheEntry& entry(mCacheEntries[i]);
+        mTotalSize -= entry.getKey()->getSize() + entry.getValue()->getSize();
+        mCacheEntries.removeAt(i);
+    }
+}
+
+bool BlobCache::isCleanable() const {
+    return mTotalSize > mMaxTotalSize / 2;
+}
+
+BlobCache::Blob::Blob(const void* data, size_t size, bool copyData):
+        mData(copyData ? malloc(size) : data),
+        mSize(size),
+        mOwnsData(copyData) {
+    if (copyData) {
+        memcpy(const_cast<void*>(mData), data, size);
+    }
+}
+
+BlobCache::Blob::~Blob() {
+    if (mOwnsData) {
+        free(const_cast<void*>(mData));
+    }
+}
+
+bool BlobCache::Blob::operator<(const Blob& rhs) const {
+    if (mSize == rhs.mSize) {
+        return memcmp(mData, rhs.mData, mSize) < 0;
+    } else {
+        return mSize < rhs.mSize;
+    }
+}
+
+const void* BlobCache::Blob::getData() const {
+    return mData;
+}
+
+size_t BlobCache::Blob::getSize() const {
+    return mSize;
+}
+
+BlobCache::CacheEntry::CacheEntry() {
+}
+
+BlobCache::CacheEntry::CacheEntry(const sp<Blob>& key, const sp<Blob>& value):
+        mKey(key),
+        mValue(value) {
+}
+
+BlobCache::CacheEntry::CacheEntry(const CacheEntry& ce):
+        mKey(ce.mKey),
+        mValue(ce.mValue) {
+}
+
+bool BlobCache::CacheEntry::operator<(const CacheEntry& rhs) const {
+    return *mKey < *rhs.mKey;
+}
+
+const BlobCache::CacheEntry& BlobCache::CacheEntry::operator=(const CacheEntry& rhs) {
+    mKey = rhs.mKey;
+    mValue = rhs.mValue;
+    return *this;
+}
+
+sp<BlobCache::Blob> BlobCache::CacheEntry::getKey() const {
+    return mKey;
+}
+
+sp<BlobCache::Blob> BlobCache::CacheEntry::getValue() const {
+    return mValue;
+}
+
+void BlobCache::CacheEntry::setValue(const sp<Blob>& value) {
+    mValue = value;
+}
+
+} // namespace android
diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp
index 8b5da0e..c748228 100644
--- a/libs/utils/Threads.cpp
+++ b/libs/utils/Threads.cpp
@@ -168,6 +168,9 @@
         return 0;
     }
 
+    // Note that *threadID is directly available to the parent only, as it is
+    // assigned after the child starts.  Use memory barrier / lock if the child
+    // or other threads also need access.
     if (threadId != NULL) {
         *threadId = (android_thread_id_t)thread; // XXX: this is not portable
     }
@@ -718,7 +721,6 @@
         res = androidCreateRawThreadEtc(_threadLoop,
                 this, name, priority, stack, &mThread);
     }
-    // The new thread wakes up at _threadLoop, but immediately blocks on mLock
     
     if (res == false) {
         mStatus = UNKNOWN_ERROR;   // something happened!
@@ -742,11 +744,6 @@
 {
     Thread* const self = static_cast<Thread*>(user);
 
-    // force a memory barrier before reading any fields, in particular mHoldSelf
-    {
-    Mutex::Autolock _l(self->mLock);
-    }
-
     sp<Thread> strong(self->mHoldSelf);
     wp<Thread> weak(strong);
     self->mHoldSelf.clear();
@@ -816,6 +813,7 @@
 
 status_t Thread::requestExitAndWait()
 {
+    Mutex::Autolock _l(mLock);
     if (mThread == getThreadId()) {
         LOGW(
         "Thread (this=%p): don't call waitForExit() from this "
@@ -825,9 +823,8 @@
         return WOULD_BLOCK;
     }
     
-    requestExit();
+    mExitPending = true;
 
-    Mutex::Autolock _l(mLock);
     while (mRunning == true) {
         mThreadExitedCondition.wait(mLock);
     }
diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk
index 72d4876..87ad98e 100644
--- a/libs/utils/tests/Android.mk
+++ b/libs/utils/tests/Android.mk
@@ -6,6 +6,7 @@
 
 # Build the unit tests.
 test_src_files := \
+	BlobCache_test.cpp \
 	ObbFile_test.cpp \
 	Looper_test.cpp \
 	String8_test.cpp \
diff --git a/libs/utils/tests/BlobCache_test.cpp b/libs/utils/tests/BlobCache_test.cpp
new file mode 100644
index 0000000..653ea5e
--- /dev/null
+++ b/libs/utils/tests/BlobCache_test.cpp
@@ -0,0 +1,257 @@
+/*
+ ** Copyright 2011, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ **     http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <utils/BlobCache.h>
+
+namespace android {
+
+class BlobCacheTest : public ::testing::Test {
+protected:
+    enum {
+        MAX_KEY_SIZE = 6,
+        MAX_VALUE_SIZE = 8,
+        MAX_TOTAL_SIZE = 13,
+    };
+
+    virtual void SetUp() {
+        mBC = new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE);
+    }
+
+    virtual void TearDown() {
+        mBC.clear();
+    }
+
+    sp<BlobCache> mBC;
+};
+
+TEST_F(BlobCacheTest, CacheSingleValueSucceeds) {
+    char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+    ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
+    ASSERT_EQ('e', buf[0]);
+    ASSERT_EQ('f', buf[1]);
+    ASSERT_EQ('g', buf[2]);
+    ASSERT_EQ('h', buf[3]);
+}
+
+TEST_F(BlobCacheTest, CacheTwoValuesSucceeds) {
+    char buf[2] = { 0xee, 0xee };
+    mBC->set("ab", 2, "cd", 2);
+    mBC->set("ef", 2, "gh", 2);
+    ASSERT_EQ(size_t(2), mBC->get("ab", 2, buf, 2));
+    ASSERT_EQ('c', buf[0]);
+    ASSERT_EQ('d', buf[1]);
+    ASSERT_EQ(size_t(2), mBC->get("ef", 2, buf, 2));
+    ASSERT_EQ('g', buf[0]);
+    ASSERT_EQ('h', buf[1]);
+}
+
+TEST_F(BlobCacheTest, GetOnlyWritesInsideBounds) {
+    char buf[6] = { 0xee, 0xee, 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+    ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf+1, 4));
+    ASSERT_EQ(0xee, buf[0]);
+    ASSERT_EQ('e', buf[1]);
+    ASSERT_EQ('f', buf[2]);
+    ASSERT_EQ('g', buf[3]);
+    ASSERT_EQ('h', buf[4]);
+    ASSERT_EQ(0xee, buf[5]);
+}
+
+TEST_F(BlobCacheTest, GetOnlyWritesIfBufferIsLargeEnough) {
+    char buf[3] = { 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+    ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 3));
+    ASSERT_EQ(0xee, buf[0]);
+    ASSERT_EQ(0xee, buf[1]);
+    ASSERT_EQ(0xee, buf[2]);
+}
+
+TEST_F(BlobCacheTest, GetDoesntAccessNullBuffer) {
+    mBC->set("abcd", 4, "efgh", 4);
+    ASSERT_EQ(size_t(4), mBC->get("abcd", 4, NULL, 0));
+}
+
+TEST_F(BlobCacheTest, MultipleSetsCacheLatestValue) {
+    char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+    mBC->set("abcd", 4, "ijkl", 4);
+    ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
+    ASSERT_EQ('i', buf[0]);
+    ASSERT_EQ('j', buf[1]);
+    ASSERT_EQ('k', buf[2]);
+    ASSERT_EQ('l', buf[3]);
+}
+
+TEST_F(BlobCacheTest, SecondSetKeepsFirstValueIfTooLarge) {
+    char buf[MAX_VALUE_SIZE+1] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+    mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1);
+    ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
+    ASSERT_EQ('e', buf[0]);
+    ASSERT_EQ('f', buf[1]);
+    ASSERT_EQ('g', buf[2]);
+    ASSERT_EQ('h', buf[3]);
+}
+
+TEST_F(BlobCacheTest, DoesntCacheIfKeyIsTooBig) {
+    char key[MAX_KEY_SIZE+1];
+    char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    for (int i = 0; i < MAX_KEY_SIZE+1; i++) {
+        key[i] = 'a';
+    }
+    mBC->set(key, MAX_KEY_SIZE+1, "bbbb", 4);
+    ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE+1, buf, 4));
+    ASSERT_EQ(0xee, buf[0]);
+    ASSERT_EQ(0xee, buf[1]);
+    ASSERT_EQ(0xee, buf[2]);
+    ASSERT_EQ(0xee, buf[3]);
+}
+
+TEST_F(BlobCacheTest, DoesntCacheIfValueIsTooBig) {
+    char buf[MAX_VALUE_SIZE+1];
+    for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
+        buf[i] = 'b';
+    }
+    mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1);
+    for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
+        buf[i] = 0xee;
+    }
+    ASSERT_EQ(size_t(0), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE+1));
+    for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
+        SCOPED_TRACE(i);
+        ASSERT_EQ(0xee, buf[i]);
+    }
+}
+
+TEST_F(BlobCacheTest, DoesntCacheIfKeyValuePairIsTooBig) {
+    // Check a testing assumptions
+    ASSERT_TRUE(MAX_TOTAL_SIZE < MAX_KEY_SIZE + MAX_VALUE_SIZE);
+    ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE);
+
+    enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE + 1 };
+
+    char key[MAX_KEY_SIZE];
+    char buf[bufSize];
+    for (int i = 0; i < MAX_KEY_SIZE; i++) {
+        key[i] = 'a';
+    }
+    for (int i = 0; i < bufSize; i++) {
+        buf[i] = 'b';
+    }
+
+    mBC->set(key, MAX_KEY_SIZE, buf, MAX_VALUE_SIZE);
+    ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE, NULL, 0));
+}
+
+TEST_F(BlobCacheTest, CacheMaxKeySizeSucceeds) {
+    char key[MAX_KEY_SIZE];
+    char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    for (int i = 0; i < MAX_KEY_SIZE; i++) {
+        key[i] = 'a';
+    }
+    mBC->set(key, MAX_KEY_SIZE, "wxyz", 4);
+    ASSERT_EQ(size_t(4), mBC->get(key, MAX_KEY_SIZE, buf, 4));
+    ASSERT_EQ('w', buf[0]);
+    ASSERT_EQ('x', buf[1]);
+    ASSERT_EQ('y', buf[2]);
+    ASSERT_EQ('z', buf[3]);
+}
+
+TEST_F(BlobCacheTest, CacheMaxValueSizeSucceeds) {
+    char buf[MAX_VALUE_SIZE];
+    for (int i = 0; i < MAX_VALUE_SIZE; i++) {
+        buf[i] = 'b';
+    }
+    mBC->set("abcd", 4, buf, MAX_VALUE_SIZE);
+    for (int i = 0; i < MAX_VALUE_SIZE; i++) {
+        buf[i] = 0xee;
+    }
+    ASSERT_EQ(size_t(MAX_VALUE_SIZE), mBC->get("abcd", 4, buf,
+            MAX_VALUE_SIZE));
+    for (int i = 0; i < MAX_VALUE_SIZE; i++) {
+        SCOPED_TRACE(i);
+        ASSERT_EQ('b', buf[i]);
+    }
+}
+
+TEST_F(BlobCacheTest, CacheMaxKeyValuePairSizeSucceeds) {
+    // Check a testing assumption
+    ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE);
+
+    enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE };
+
+    char key[MAX_KEY_SIZE];
+    char buf[bufSize];
+    for (int i = 0; i < MAX_KEY_SIZE; i++) {
+        key[i] = 'a';
+    }
+    for (int i = 0; i < bufSize; i++) {
+        buf[i] = 'b';
+    }
+
+    mBC->set(key, MAX_KEY_SIZE, buf, bufSize);
+    ASSERT_EQ(size_t(bufSize), mBC->get(key, MAX_KEY_SIZE, NULL, 0));
+}
+
+TEST_F(BlobCacheTest, CacheMinKeyAndValueSizeSucceeds) {
+    char buf[1] = { 0xee };
+    mBC->set("x", 1, "y", 1);
+    ASSERT_EQ(size_t(1), mBC->get("x", 1, buf, 1));
+    ASSERT_EQ('y', buf[0]);
+}
+
+TEST_F(BlobCacheTest, CacheSizeDoesntExceedTotalLimit) {
+    for (int i = 0; i < 256; i++) {
+        uint8_t k = i;
+        mBC->set(&k, 1, "x", 1);
+    }
+    int numCached = 0;
+    for (int i = 0; i < 256; i++) {
+        uint8_t k = i;
+        if (mBC->get(&k, 1, NULL, 0) == 1) {
+            numCached++;
+        }
+    }
+    ASSERT_GE(MAX_TOTAL_SIZE / 2, numCached);
+}
+
+TEST_F(BlobCacheTest, ExceedingTotalLimitHalvesCacheSize) {
+    // Fill up the entire cache with 1 char key/value pairs.
+    const int maxEntries = MAX_TOTAL_SIZE / 2;
+    for (int i = 0; i < maxEntries; i++) {
+        uint8_t k = i;
+        mBC->set(&k, 1, "x", 1);
+    }
+    // Insert one more entry, causing a cache overflow.
+    {
+        uint8_t k = maxEntries;
+        mBC->set(&k, 1, "x", 1);
+    }
+    // Count the number of entries in the cache.
+    int numCached = 0;
+    for (int i = 0; i < maxEntries+1; i++) {
+        uint8_t k = i;
+        if (mBC->get(&k, 1, NULL, 0) == 1) {
+            numCached++;
+        }
+    }
+    ASSERT_EQ(maxEntries/2 + 1, numCached);
+}
+
+} // namespace android
diff --git a/tests/GridLayoutTest/res/layout/grid3.xml b/tests/GridLayoutTest/res/layout/grid3.xml
index 31dc75a..5cdacf7 100644
--- a/tests/GridLayoutTest/res/layout/grid3.xml
+++ b/tests/GridLayoutTest/res/layout/grid3.xml
@@ -19,14 +19,17 @@
 
         android:layout_width="match_parent"
         android:layout_height="match_parent"
+
         android:useDefaultMargins="true"
         android:marginsIncludedInAlignment="false"
+
         android:columnCount="4"
         >
 
     <TextView
             android:text="Email account"
             android:textSize="48dip"
+
             android:layout_columnSpan="4"
             android:layout_gravity="center_horizontal"
             />
@@ -34,12 +37,14 @@
     <TextView
             android:text="You can configure email in just a few steps:"
             android:textSize="20dip"
+
             android:layout_columnSpan="4"
             android:layout_gravity="left"
             />
 
     <TextView
             android:text="Email address:"
+
             android:layout_gravity="right"
             />
 
@@ -49,6 +54,7 @@
 
     <TextView
             android:text="Password:"
+
             android:layout_column="0"
             android:layout_gravity="right"
             />
@@ -58,14 +64,15 @@
             />
 
     <Space
-            android:layout_rowWeight="1"
-            android:layout_columnWeight="1"
             android:layout_row="4"
             android:layout_column="2"
+            android:layout_rowWeight="1"
+            android:layout_columnWeight="1"
             />
 
     <Button
             android:text="Manual setup"
+
             android:layout_row="5"
             android:layout_column="3"
             android:layout_gravity="fill_horizontal"
@@ -73,6 +80,7 @@
 
     <Button
             android:text="Next"
+
             android:layout_column="3"
             android:layout_gravity="fill_horizontal"
             />
diff --git a/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java b/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java
index 5e29cf1..32365d7 100644
--- a/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java
+++ b/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java
@@ -20,11 +20,15 @@
 import android.content.Context;
 import android.os.Bundle;
 import android.view.View;
-
-import android.widget.*;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.GridLayout;
+import android.widget.Space;
+import android.widget.TextView;
 
 import static android.text.InputType.TYPE_CLASS_TEXT;
-import static android.view.inputmethod.EditorInfo.*;
+import static android.view.inputmethod.EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
+import static android.view.inputmethod.EditorInfo.TYPE_TEXT_VARIATION_PASSWORD;
 import static android.widget.GridLayout.*;
 
 public class Activity2 extends Activity {
@@ -42,12 +46,12 @@
         Group row6 = new Group(6, CENTER);
         Group row7 = new Group(7, CENTER);
 
-        Group col1a = new Group(1, 5, CENTER);
-        Group col1b = new Group(1, 5, LEFT);
+        Group col1a = new Group(1, 4, CENTER);
+        Group col1b = new Group(1, 4, LEFT);
         Group col1c = new Group(1, RIGHT);
-        Group col2 =  new Group(2, LEFT);
-        Group col3 =  new Group(3, FILL);
-        Group col4 =  new Group(4, FILL);
+        Group col2 = new Group(2, LEFT);
+        Group col3 = new Group(3, FILL);
+        Group col4 = new Group(4, FILL);
 
         {
             TextView v = new TextView(context);
@@ -55,20 +59,17 @@
             v.setText("Email setup");
             vg.addView(v, new LayoutParams(row1, col1a));
         }
-
         {
             TextView v = new TextView(context);
             v.setTextSize(20);
             v.setText("You can configure email in just a few steps:");
             vg.addView(v, new LayoutParams(row2, col1b));
         }
-
         {
             TextView v = new TextView(context);
             v.setText("Email address:");
             vg.addView(v, new LayoutParams(row3, col1c));
         }
-
         {
             EditText v = new EditText(context);
             v.setInputType(TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
@@ -78,13 +79,11 @@
                 vg.addView(v, lp);
             }
         }
-
         {
             TextView v = new TextView(context);
             v.setText("Password:");
             vg.addView(v, new LayoutParams(row4, col1c));
         }
-
         {
             TextView v = new EditText(context);
             v.setInputType(TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_PASSWORD);
@@ -94,7 +93,6 @@
                 vg.addView(v, lp);
             }
         }
-
         {
             Space v = new Space(context);
             {
@@ -104,13 +102,11 @@
                 vg.addView(v, lp);
             }
         }
-
         {
             Button v = new Button(context);
             v.setText("Manual setup");
             vg.addView(v, new LayoutParams(row6, col4));
         }
-
         {
             Button v = new Button(context);
             v.setText("Next");
diff --git a/wifi/java/android/net/wifi/SupplicantState.java b/wifi/java/android/net/wifi/SupplicantState.java
index 91e685f..509b02c 100644
--- a/wifi/java/android/net/wifi/SupplicantState.java
+++ b/wifi/java/android/net/wifi/SupplicantState.java
@@ -216,6 +216,28 @@
         }
     }
 
+    static boolean isDriverActive(SupplicantState state) {
+        switch(state) {
+            case DISCONNECTED:
+            case DORMANT:
+            case INACTIVE:
+            case AUTHENTICATING:
+            case ASSOCIATING:
+            case ASSOCIATED:
+            case SCANNING:
+            case FOUR_WAY_HANDSHAKE:
+            case GROUP_HANDSHAKE:
+            case COMPLETED:
+                return true;
+            case INTERFACE_DISABLED:
+            case UNINITIALIZED:
+            case INVALID:
+                return false;
+            default:
+                throw new IllegalArgumentException("Unknown supplicant state");
+        }
+    }
+
     /** Implement the Parcelable interface {@hide} */
     public int describeContents() {
         return 0;
diff --git a/wifi/java/android/net/wifi/WifiMonitor.java b/wifi/java/android/net/wifi/WifiMonitor.java
index 4a45825..4ec4cfc 100644
--- a/wifi/java/android/net/wifi/WifiMonitor.java
+++ b/wifi/java/android/net/wifi/WifiMonitor.java
@@ -107,7 +107,7 @@
      * <pre>
      * CTRL-EVENT-DRIVER-STATE state
      * </pre>
-     * <code>state</code> is either STARTED or STOPPED
+     * <code>state</code> can be HANGED
      */
     private static final String driverStateEvent = "DRIVER-STATE";
     /**
@@ -304,11 +304,7 @@
             if (state == null) {
                 return;
             }
-            if (state.equals("STOPPED")) {
-                mWifiStateMachine.notifyDriverStopped();
-            } else if (state.equals("STARTED")) {
-                mWifiStateMachine.notifyDriverStarted();
-            } else if (state.equals("HANGED")) {
+            if (state.equals("HANGED")) {
                 mWifiStateMachine.notifyDriverHung();
             }
         }
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 6df37bb..3df3736 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -212,22 +212,18 @@
     static final int SUP_CONNECTION_EVENT                 = BASE + 31;
     /* Connection to supplicant lost */
     static final int SUP_DISCONNECTION_EVENT              = BASE + 32;
-    /* Driver start completed */
-    static final int DRIVER_START_EVENT                   = BASE + 33;
-    /* Driver stop completed */
-    static final int DRIVER_STOP_EVENT                    = BASE + 34;
-    /* Network connection completed */
-    static final int NETWORK_CONNECTION_EVENT             = BASE + 36;
+   /* Network connection completed */
+    static final int NETWORK_CONNECTION_EVENT             = BASE + 33;
     /* Network disconnection completed */
-    static final int NETWORK_DISCONNECTION_EVENT          = BASE + 37;
+    static final int NETWORK_DISCONNECTION_EVENT          = BASE + 34;
     /* Scan results are available */
-    static final int SCAN_RESULTS_EVENT                   = BASE + 38;
+    static final int SCAN_RESULTS_EVENT                   = BASE + 35;
     /* Supplicate state changed */
-    static final int SUPPLICANT_STATE_CHANGE_EVENT        = BASE + 39;
+    static final int SUPPLICANT_STATE_CHANGE_EVENT        = BASE + 36;
     /* Password failure and EAP authentication failure */
-    static final int AUTHENTICATION_FAILURE_EVENT         = BASE + 40;
+    static final int AUTHENTICATION_FAILURE_EVENT         = BASE + 37;
     /* WPS overlap detected */
-    static final int WPS_OVERLAP_EVENT                    = BASE + 41;
+    static final int WPS_OVERLAP_EVENT                    = BASE + 38;
 
 
     /* Supplicant commands */
@@ -1421,6 +1417,35 @@
         return mNetworkInfo.getDetailedState();
     }
 
+
+    private SupplicantState handleSupplicantStateChange(Message message) {
+        StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+        SupplicantState state = stateChangeResult.state;
+        // Supplicant state change
+        // [31-13] Reserved for future use
+        // [8 - 0] Supplicant state (as defined in SupplicantState.java)
+        // 50023 supplicant_state_changed (custom|1|5)
+        EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, state.ordinal());
+        mWifiInfo.setSupplicantState(state);
+        // Network id is only valid when we start connecting
+        if (SupplicantState.isConnecting(state)) {
+            mWifiInfo.setNetworkId(stateChangeResult.networkId);
+        } else {
+            mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
+        }
+
+        if (state == SupplicantState.ASSOCIATING) {
+            /* BSSID is valid only in ASSOCIATING state */
+            mWifiInfo.setBSSID(stateChangeResult.BSSID);
+        }
+        setNetworkDetailedState(WifiInfo.getDetailedStateOf(state));
+
+        mSupplicantStateTracker.sendMessage(Message.obtain(message));
+        mWpsStateMachine.sendMessage(Message.obtain(message));
+
+        return state;
+    }
+
     /**
      * Resets the Wi-Fi Connections by clearing any state, resetting any sockets
      * using the interface, stopping DHCP & disabling interface
@@ -1674,14 +1699,6 @@
         sendMessage(SCAN_RESULTS_EVENT);
     }
 
-    void notifyDriverStarted() {
-        sendMessage(DRIVER_START_EVENT);
-    }
-
-    void notifyDriverStopped() {
-        sendMessage(DRIVER_STOP_EVENT);
-    }
-
     void notifyDriverHung() {
         setWifiEnabled(false);
         setWifiEnabled(true);
@@ -1737,8 +1754,6 @@
                 case CMD_REASSOCIATE:
                 case SUP_CONNECTION_EVENT:
                 case SUP_DISCONNECTION_EVENT:
-                case DRIVER_START_EVENT:
-                case DRIVER_STOP_EVENT:
                 case NETWORK_CONNECTION_EVENT:
                 case NETWORK_DISCONNECTION_EVENT:
                 case SCAN_RESULTS_EVENT:
@@ -2284,13 +2299,19 @@
         public boolean processMessage(Message message) {
             if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
             switch(message.what) {
-                case DRIVER_START_EVENT:
-                    transitionTo(mDriverStartedState);
+               case SUPPLICANT_STATE_CHANGE_EVENT:
+                    SupplicantState state = handleSupplicantStateChange(message);
+                    /* If suplicant is exiting out of INTERFACE_DISABLED state into
+                     * a state that indicates driver has started, it is ready to
+                     * receive driver commands
+                     */
+                    if (SupplicantState.isDriverActive(state)) {
+                        transitionTo(mDriverStartedState);
+                    }
                     break;
                     /* Queue driver commands & connection events */
                 case CMD_START_DRIVER:
                 case CMD_STOP_DRIVER:
-                case SUPPLICANT_STATE_CHANGE_EVENT:
                 case NETWORK_CONNECTION_EVENT:
                 case NETWORK_DISCONNECTION_EVENT:
                 case AUTHENTICATION_FAILURE_EVENT:
@@ -2429,8 +2450,11 @@
         public boolean processMessage(Message message) {
             if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
             switch(message.what) {
-                case DRIVER_STOP_EVENT:
-                    transitionTo(mDriverStoppedState);
+                case SUPPLICANT_STATE_CHANGE_EVENT:
+                    SupplicantState state = handleSupplicantStateChange(message);
+                    if (state == SupplicantState.INTERFACE_DISABLED) {
+                        transitionTo(mDriverStoppedState);
+                    }
                     break;
                     /* Queue driver commands */
                 case CMD_START_DRIVER:
@@ -2465,11 +2489,23 @@
         public boolean processMessage(Message message) {
             if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
             switch (message.what) {
-                case CMD_START_DRIVER:
-                    mWakeLock.acquire();
-                    WifiNative.startDriverCommand();
-                    transitionTo(mDriverStartingState);
-                    mWakeLock.release();
+               case CMD_START_DRIVER:
+                   mWakeLock.acquire();
+                   WifiNative.startDriverCommand();
+                   mWakeLock.release();
+                   break;
+                case SUPPLICANT_STATE_CHANGE_EVENT:
+                    SupplicantState state = handleSupplicantStateChange(message);
+                    /* A driver start causes supplicant to first report an INTERFACE_DISABLED
+                     * state before transitioning out of it for connection. Stay in
+                     * DriverStoppedState until we get an INTERFACE_DISABLED state and transition
+                     * to DriverStarting upon getting that
+                     * TODO: Fix this when the supplicant can be made to just transition out of
+                     * INTERFACE_DISABLED state when driver gets started
+                     */
+                    if (state == SupplicantState.INTERFACE_DISABLED) {
+                        transitionTo(mDriverStartingState);
+                    }
                     break;
                 default:
                     return NOT_HANDLED;
@@ -2535,29 +2571,8 @@
                     sendErrorBroadcast(WifiManager.WPS_OVERLAP_ERROR);
                     break;
                 case SUPPLICANT_STATE_CHANGE_EVENT:
-                    stateChangeResult = (StateChangeResult) message.obj;
-                    SupplicantState state = stateChangeResult.state;
-                    // Supplicant state change
-                    // [31-13] Reserved for future use
-                    // [8 - 0] Supplicant state (as defined in SupplicantState.java)
-                    // 50023 supplicant_state_changed (custom|1|5)
-                    EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, state.ordinal());
-                    mWifiInfo.setSupplicantState(state);
-                    // Network id is only valid when we start connecting
-                    if (SupplicantState.isConnecting(state)) {
-                        mWifiInfo.setNetworkId(stateChangeResult.networkId);
-                    } else {
-                        mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
-                    }
-
-                    if (state == SupplicantState.ASSOCIATING) {
-                        /* BSSID is valid only in ASSOCIATING state */
-                        mWifiInfo.setBSSID(stateChangeResult.BSSID);
-                    }
-
-                    mSupplicantStateTracker.sendMessage(Message.obtain(message));
-                    mWpsStateMachine.sendMessage(Message.obtain(message));
-                    break;
+                    handleSupplicantStateChange(message);
+                   break;
                     /* Do a redundant disconnect without transition */
                 case CMD_DISCONNECT:
                     WifiNative.disconnectCommand();
@@ -2964,12 +2979,7 @@
                     /* Ignore network disconnect */
                 case NETWORK_DISCONNECTION_EVENT:
                     break;
-                case SUPPLICANT_STATE_CHANGE_EVENT:
-                    StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
-                    setNetworkDetailedState(WifiInfo.getDetailedStateOf(stateChangeResult.state));
-                    /* DriverStartedState does the rest of the handling */
-                    return NOT_HANDLED;
-                case CMD_START_SCAN:
+               case CMD_START_SCAN:
                     /* Disable background scan temporarily during a regular scan */
                     if (mEnableBackgroundScan) {
                         WifiNative.enableBackgroundScanCommand(false);