Merge "Init car mode when phone is booted in a dock."
diff --git a/common/java/com/android/common/speech/Recognition.java b/common/java/com/android/common/speech/Recognition.java
index 6f164a9..bf60c9a 100644
--- a/common/java/com/android/common/speech/Recognition.java
+++ b/common/java/com/android/common/speech/Recognition.java
@@ -19,7 +19,8 @@
 /**
  * Utilities for voice recognition implementations.
  *
- * @see android.app.RecognitionService
+ * @see android.speech.RecognitionService
+ * @see android.speech.RecognizerIntent
  */
 public class Recognition {
 
@@ -30,7 +31,39 @@
      * is set by anyone but the system process, it should be overridden by the voice search
      * implementation.
      */
-    public final static String EXTRA_CALLING_PACKAGE = "calling_package";
+    public static final String EXTRA_CALLING_PACKAGE = "calling_package";
+    
+    /**
+     * The key to the extra in the Bundle returned by
+     * android.speech.RecognizerIntent#ACTION_GET_LANGUAGE_DETAILS
+     * which is an ArrayList of CharSequences which are hints that can be shown to
+     * the user for voice actions currently supported by voice search for the user's current
+     * language preference for voice search (i.e., the one defined in the extra
+     * android.speech.RecognizerIntent#EXTRA_LANGUAGE_PREFERENCE).
+     *
+     * If this is paired with EXTRA_HINT_CONTEXT, should return a set of hints that are
+     * appropriate for the provided context.
+     *
+     * The CharSequences are SpannedStrings and will contain segments wrapped in
+     * <annotation action="true"></annotation>. This is to indicate the section of the text
+     * which represents the voice action, to be highlighted in the UI if so desired.
+     */
+    public static final String EXTRA_HINT_STRINGS = "android.speech.extra.HINT_STRINGS";
+    
+    /**
+     * The key to an extra to be included in the request intent for
+     * android.speech.RecognizerIntent#ACTION_GET_LANGUAGE_DETAILS.
+     * Should be an int of one of the values defined below. If an
+     * unknown int value is provided, it should be ignored.
+     */
+    public static final String EXTRA_HINT_CONTEXT = "android.speech.extra.HINT_CONTEXT";
+    
+    /**
+     * A set of values for EXTRA_HINT_CONTEXT.
+     */
+    public static final int HINT_CONTEXT_UNKNOWN = 0;
+    public static final int HINT_CONTEXT_VOICE_SEARCH_HELP = 1;
+    public static final int HINT_CONTEXT_CAR_HOME = 2;
 
     private Recognition() { }   // don't instantiate
 }
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index 6a02a58..cb6aab6 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -313,7 +313,6 @@
         mLaunchComponent = null;
         mAppSearchData = null;
         mSearchable = null;
-        mActivityContext = null;
         mUserQuery = null;
     }
 
@@ -411,7 +410,7 @@
             updateSearchAppIcon();
             updateSearchBadge();
             updateQueryHint();
-            updateVoiceButton();
+            updateVoiceButton(TextUtils.isEmpty(mUserQuery));
             
             // In order to properly configure the input method (if one is being used), we
             // need to let it know if we'll be providing suggestions.  Although it would be
@@ -560,10 +559,13 @@
     /**
      * Update the visibility of the voice button.  There are actually two voice search modes, 
      * either of which will activate the button.
+     * @param empty whether the search query text field is empty. If it is, then the other
+     * criteria apply to make the voice button visible. Otherwise the voice button will not
+     * be visible - i.e., if the user has typed a query, remove the voice button.
      */
-    private void updateVoiceButton() {
+    private void updateVoiceButton(boolean empty) {
         int visibility = View.GONE;
-        if (mSearchable.getVoiceSearchEnabled()) {
+        if (mSearchable.getVoiceSearchEnabled() && empty) {
             Intent testIntent = null;
             if (mSearchable.getVoiceSearchLaunchWebSearch()) {
                 testIntent = mVoiceWebSearchIntent;
@@ -666,6 +668,7 @@
                 // The user changed the query, remember it.
                 mUserQuery = s == null ? "" : s.toString();
             }
+            updateVoiceButton(TextUtils.isEmpty(s));
         }
 
         public void afterTextChanged(Editable s) {
@@ -746,9 +749,6 @@
                 return;
             }
             SearchableInfo searchable = mSearchable;
-            // First stop the existing search before starting voice search, or else we'll end
-            // up showing the search dialog again once we return to the app.
-            cancel();
             try {
                 if (searchable.getVoiceSearchLaunchWebSearch()) {
                     getContext().startActivity(mVoiceWebSearchIntent);
@@ -762,6 +762,7 @@
                 // voice search before showing the button. But just in case...
                 Log.w(LOG_TAG, "Could not find voice search activity");
             }
+            dismiss();
          }
     };
     
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f6f5235..679206d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7336,6 +7336,7 @@
      * Sets the background color for this view.
      * @param color the color of the background
      */
+    @RemotableViewMethod
     public void setBackgroundColor(int color) {
         setBackgroundDrawable(new ColorDrawable(color));
     }
@@ -7346,6 +7347,7 @@
      * @param resid The identifier of the resource.
      * @attr ref android.R.styleable#View_background
      */
+    @RemotableViewMethod
     public void setBackgroundResource(int resid) {
         if (resid != 0 && resid == mBackgroundResource) {
             return;
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index 94bedde..84e34bc 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -150,8 +150,13 @@
         }
 
         boolean exactMatch(Cookie in) {
+            // An exact match means that domain, path, and name are equal. If
+            // both values are null, the cookies match. If both values are
+            // non-null, the cookies match. If one value is null and the other
+            // is non-null, the cookies do not match (i.e. "foo=;" and "foo;")
+            boolean valuesMatch = !((value == null) ^ (in.value == null));
             return domain.equals(in.domain) && path.equals(in.path) &&
-                    name.equals(in.name);
+                    name.equals(in.name) && valuesMatch;
         }
 
         boolean domainMatch(String urlHost) {
@@ -206,17 +211,29 @@
             // As Set is not modified if the two objects are same, we do want to
             // assign different value for each cookie.
             int diff = cookie2.path.length() - cookie1.path.length();
-            if (diff == 0) {
-                diff = cookie2.domain.length() - cookie1.domain.length();
-                if (diff == 0) {
-                    diff = cookie2.name.hashCode() - cookie1.name.hashCode();
-                    if (diff == 0) {
-                        Log.w(LOGTAG, "Found two cookies with the same value." +
-                                "cookie1=" + cookie1 + " , cookie2=" + cookie2);
-                    }
-                }
+            if (diff != 0) return diff;
+
+            diff = cookie2.domain.length() - cookie1.domain.length();
+            if (diff != 0) return diff;
+
+            diff = cookie2.name.hashCode() - cookie1.name.hashCode();
+            if (diff != 0) return diff;
+
+            // If cookie2 has a null value, it should come later in
+            // the list.
+            if (cookie2.value == null) {
+                return -1;
+            } else if (cookie1.value == null) {
+                // Now we know that cookie2 does not have a null value, if
+                // cookie1 has a null value, place it later in the list.
+                return 1;
             }
-            return diff;
+
+            // cookie1 and cookie2 both have non-null values so we emit a
+            // warning and treat them as the same.
+            Log.w(LOGTAG, "Found two cookies with the same value."
+                    + "cookie1=" + cookie1 + " , cookie2=" + cookie2);
+            return 0;
         }
     }
 
@@ -459,8 +476,10 @@
             }
 
             ret.append(cookie.name);
-            ret.append(EQUAL);
-            ret.append(cookie.value);
+            if (cookie.value != null) {
+                ret.append(EQUAL);
+                ret.append(cookie.value);
+            }
         }
 
         if (ret.length() > 0) {
@@ -634,7 +653,10 @@
                         byteCount += cookie.domain.length()
                                 + cookie.path.length()
                                 + cookie.name.length()
-                                + cookie.value.length() + 14;
+                                + (cookie.value != null
+                                        ? cookie.value.length()
+                                        : 0)
+                                + 14;
                         count++;
                     }
                 } else {
@@ -779,38 +801,45 @@
              */
             int semicolonIndex = cookieString.indexOf(SEMICOLON, index);
             int equalIndex = cookieString.indexOf(EQUAL, index);
-            if (equalIndex == -1) {
-                // bad format, force return
-                break;
-            }
-            if (semicolonIndex > -1 && semicolonIndex < equalIndex) {
-                // empty cookie, like "; path=/", return
-                break;
-            }
             cookie = new Cookie(host, path);
-            cookie.name = cookieString.substring(index, equalIndex);
-            if (cookieString.charAt(equalIndex + 1) == QUOTATION) {
-                index = cookieString.indexOf(QUOTATION, equalIndex + 2);
-                if (index == -1) {
-                    // bad format, force return
-                    break;
+
+            // Cookies like "testcookie; path=/;" are valid and used
+            // (lovefilm.se). Check for equal as in the string "testcookie"
+            // Check for equalIndex == -1 as in the string "testcookie;"
+            if (semicolonIndex <= equalIndex || equalIndex == -1) {
+                // Fix up the index in case we have a string like "testcookie"
+                if (semicolonIndex == -1) {
+                    semicolonIndex = length;
                 }
-            }
-            semicolonIndex = cookieString.indexOf(SEMICOLON, index);
-            if (semicolonIndex == -1) {
-                semicolonIndex = length;
-            }
-            if (semicolonIndex - equalIndex > MAX_COOKIE_LENGTH) {
-                // cookie is too big, trim it
-                cookie.value = cookieString.substring(equalIndex + 1,
-                        equalIndex + MAX_COOKIE_LENGTH);
-            } else if (equalIndex + 1 == semicolonIndex
-                    || semicolonIndex < equalIndex) {
-                // these are unusual case like foo=; and foo; path=/
-                cookie.value = "";
+                cookie.name = cookieString.substring(index, semicolonIndex);
+                cookie.value = null;
             } else {
-                cookie.value = cookieString.substring(equalIndex + 1,
-                        semicolonIndex);
+                cookie.name = cookieString.substring(index, equalIndex);
+                if (cookieString.charAt(equalIndex + 1) == QUOTATION) {
+                    index = cookieString.indexOf(QUOTATION, equalIndex + 2);
+                    if (index == -1) {
+                        // bad format, force return
+                        break;
+                    }
+                }
+                // Get the semicolon index again in case it was contained within
+                // the quotations.
+                semicolonIndex = cookieString.indexOf(SEMICOLON, index);
+                if (semicolonIndex == -1) {
+                    semicolonIndex = length;
+                }
+                if (semicolonIndex - equalIndex > MAX_COOKIE_LENGTH) {
+                    // cookie is too big, trim it
+                    cookie.value = cookieString.substring(equalIndex + 1,
+                            equalIndex + 1 + MAX_COOKIE_LENGTH);
+                } else if (equalIndex + 1 == semicolonIndex
+                        || semicolonIndex < equalIndex) {
+                    // this is an unusual case like foo=;
+                    cookie.value = "";
+                } else {
+                    cookie.value = cookieString.substring(equalIndex + 1,
+                            semicolonIndex);
+                }
             }
             // get attributes
             index = semicolonIndex;
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 67543aa..b13fc75 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -213,7 +213,6 @@
     static private final boolean AUTO_REDRAW_HACK = false;
     // true means redraw the screen all-the-time. Only with AUTO_REDRAW_HACK
     private boolean mAutoRedraw;
-    private int mRootLayer; // C++ pointer to the root layer
 
     static final String LOGTAG = "webview";
 
@@ -618,6 +617,12 @@
     private static final int SNAP_Y = 4; // may be combined with SNAP_LOCK
     private boolean mSnapPositive;
 
+    // keep these in sync with their counterparts in WebView.cpp
+    private static final int DRAW_EXTRAS_NONE = 0;
+    private static final int DRAW_EXTRAS_FIND = 1;
+    private static final int DRAW_EXTRAS_SELECTION = 2;
+    private static final int DRAW_EXTRAS_CURSOR_RING = 3;
+
     // Used to match key downs and key ups
     private boolean mGotKeyDown;
 
@@ -1306,6 +1311,7 @@
                 // onSizeChanged() is called, the rest will be set
                 // correctly
                 mActualScale = scale;
+                mInvActualScale = 1 / scale;
                 mTextWrapScale = b.getFloat("textwrapScale", scale);
                 mInZoomOverview = b.getBoolean("overview");
                 invalidate();
@@ -3116,7 +3122,7 @@
         int mScrollY;
         int mWidth;
         int mHeight;
-        float mScale;
+        float mInvScale;
     }
 
     private Metrics getViewMetrics() {
@@ -3125,23 +3131,21 @@
         metrics.mScrollY = computeVerticalScrollOffset();
         metrics.mWidth = getWidth();
         metrics.mHeight = getHeight() - getVisibleTitleHeight();
-        metrics.mScale = mActualScale;
+        metrics.mInvScale = mInvActualScale;
         return metrics;
     }
 
-    private void drawLayers(Canvas canvas) {
-        if (mRootLayer != 0) {
-            // Currently for each draw we compute the animation values;
-            // We may in the future decide to do that independently.
-            if (nativeEvaluateLayersAnimations(mRootLayer)) {
-                // If we have unfinished (or unstarted) animations,
-                // we ask for a repaint.
-                invalidate();
-            }
-
-            // We can now draw the layers.
-            nativeDrawLayers(mRootLayer, canvas);
+    private void drawExtras(Canvas canvas, int extras) {
+        if (mNativeClass == 0) return;
+        // Currently for each draw we compute the animation values;
+        // We may in the future decide to do that independently.
+        if (nativeEvaluateLayersAnimations()) {
+            // If we have unfinished (or unstarted) animations,
+            // we ask for a repaint.
+            invalidate();
         }
+
+        nativeDrawExtras(canvas, extras);
     }
 
     private void drawCoreAndCursorRing(Canvas canvas, int color,
@@ -3149,7 +3153,7 @@
         if (mDrawHistory) {
             canvas.scale(mActualScale, mActualScale);
             canvas.drawPicture(mHistoryPicture);
-            drawLayers(canvas);
+            drawExtras(canvas, DRAW_EXTRAS_NONE);
             return;
         }
 
@@ -3228,27 +3232,29 @@
 
         mWebViewCore.drawContentPicture(canvas, color,
                 (animateZoom || mPreviewZoomOnly), animateScroll);
-        boolean cursorIsInLayer = nativeCursorIsInLayer();
-        if (drawCursorRing && !cursorIsInLayer) {
-            nativeDrawCursorRing(canvas);
-        }
-        // When the FindDialog is up, only draw the matches if we are not in
-        // the process of scrolling them into view.
-        if (mFindIsUp && !animateScroll) {
-            nativeDrawMatches(canvas);
-        }
-        drawLayers(canvas);
-
         if (mNativeClass == 0) return;
-        if (mShiftIsPressed && !(animateZoom || mPreviewZoomOnly)) {
-            if (mTouchSelection || mExtendSelection) {
-                nativeDrawSelectionRegion(canvas);
+        // decide which adornments to draw
+        int extras = DRAW_EXTRAS_NONE;
+        if (mFindIsUp) {
+            // When the FindDialog is up, only draw the matches if we are not in
+            // the process of scrolling them into view.
+            if (!animateScroll) {
+                extras = DRAW_EXTRAS_FIND;
             }
-            if (!mTouchSelection) {
-                nativeDrawSelectionPointer(canvas, mInvActualScale, mSelectX,
-                        mSelectY - getTitleHeight(), mExtendSelection);
+        } else if (mShiftIsPressed) {
+            if (!animateZoom && !mPreviewZoomOnly) {
+                extras = DRAW_EXTRAS_SELECTION;
+                nativeSetSelectionRegion(mTouchSelection || mExtendSelection);
+                nativeSetSelectionPointer(!mTouchSelection, mInvActualScale,
+                        mSelectX, mSelectY - getTitleHeight(),
+                        mExtendSelection);
             }
         } else if (drawCursorRing) {
+            extras = DRAW_EXTRAS_CURSOR_RING;
+        }
+        drawExtras(canvas, extras);
+
+        if (extras == DRAW_EXTRAS_CURSOR_RING) {
             if (mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
                 mTouchMode = TOUCH_SHORTPRESS_MODE;
                 HitTestResult hitTest = getHitTestResult();
@@ -3259,7 +3265,6 @@
                             LONG_PRESS_TIMEOUT);
                 }
             }
-            if (cursorIsInLayer) nativeDrawCursorRing(canvas);
         }
         if (mFocusSizeChanged) {
             mFocusSizeChanged = false;
@@ -3364,6 +3369,7 @@
         if (isTextView) {
             rebuildWebTextView();
             if (inEditingMode()) {
+                mWebTextView.setDefaultSelection();
                 imm.showSoftInput(mWebTextView, 0);
                 if (zoom) {
                     didUpdateTextViewBounds(true);
@@ -3681,6 +3687,7 @@
             // might be.  Check it, and if so, hand over to the WebTextView.
             rebuildWebTextView();
             if (inEditingMode()) {
+                mWebTextView.setDefaultSelection();
                 return mWebTextView.dispatchKeyEvent(event);
             }
         }
@@ -6001,12 +6008,7 @@
                     break;
                 }
                 case SET_ROOT_LAYER_MSG_ID: {
-                    int oldLayer = mRootLayer;
-                    mRootLayer = msg.arg1;
-                    nativeSetRootLayer(mRootLayer);
-                    if (oldLayer > 0) {
-                        nativeDestroyLayer(oldLayer);
-                    }
+                    nativeSetRootLayer(msg.arg1);
                     invalidate();
                     break;
                 }
@@ -6766,7 +6768,6 @@
     /* package */ native boolean nativeCursorMatchesFocus();
     private native boolean  nativeCursorIntersects(Rect visibleRect);
     private native boolean  nativeCursorIsAnchor();
-    private native boolean  nativeCursorIsInLayer();
     private native boolean  nativeCursorIsTextInput();
     private native Point    nativeCursorPosition();
     private native String   nativeCursorText();
@@ -6777,14 +6778,8 @@
     private native boolean  nativeCursorWantsKeyEvents();
     private native void     nativeDebugDump();
     private native void     nativeDestroy();
-    private native void     nativeDrawCursorRing(Canvas content);
-    private native void     nativeDestroyLayer(int layer);
-    private native boolean  nativeEvaluateLayersAnimations(int layer);
-    private native void     nativeDrawLayers(int layer, Canvas canvas);
-    private native void     nativeDrawMatches(Canvas canvas);
-    private native void     nativeDrawSelectionPointer(Canvas content,
-            float scale, int x, int y, boolean extendSelection);
-    private native void     nativeDrawSelectionRegion(Canvas content);
+    private native boolean  nativeEvaluateLayersAnimations();
+    private native void     nativeDrawExtras(Canvas canvas, int extra);
     private native void     nativeDumpDisplayTree(String urlOrNull);
     private native int      nativeFindAll(String findLower, String findUpper);
     private native void     nativeFindNext(boolean forward);
@@ -6831,6 +6826,9 @@
     private native void     nativeSetFollowedLink(boolean followed);
     private native void     nativeSetHeightCanMeasure(boolean measure);
     private native void     nativeSetRootLayer(int layer);
+    private native void     nativeSetSelectionPointer(boolean set,
+            float scale, int x, int y, boolean extendSelection);
+    private native void     nativeSetSelectionRegion(boolean set);
     private native int      nativeTextGeneration();
     // Never call this version except by updateCachedTextfield(String) -
     // we always want to pass in our generation number.
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index 4bd3a82..bd07e1f 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -632,6 +632,8 @@
 
         final boolean baselineAligned = mBaselineAligned;
         final boolean useLargestChild = mUseLargestChild;
+        
+        final boolean isExactly = widthMode == MeasureSpec.EXACTLY;
 
         int largestChildWidth = Integer.MIN_VALUE;
 
@@ -658,7 +660,13 @@
                 // Optimization: don't bother measuring children who are going to use
                 // leftover space. These views will get measured again down below if
                 // there is any leftover space.
-                mTotalLength += lp.leftMargin + lp.rightMargin;
+                if (isExactly) {
+                    mTotalLength += lp.leftMargin + lp.rightMargin;
+                } else {
+                    final int totalLength = mTotalLength;
+                    mTotalLength = Math.max(totalLength, totalLength +
+                            lp.leftMargin + lp.rightMargin);
+                }
 
                 // Baseline alignment requires to measure widgets to obtain the
                 // baseline offset (in particular for TextViews). The following
@@ -694,8 +702,14 @@
                 }
 
                 final int childWidth = child.getMeasuredWidth();
-                mTotalLength += childWidth + lp.leftMargin + lp.rightMargin +
-                        getNextLocationOffset(child);
+                if (isExactly) {
+                    mTotalLength += childWidth + lp.leftMargin + lp.rightMargin +
+                            getNextLocationOffset(child);
+                } else {
+                    final int totalLength = mTotalLength;
+                    mTotalLength = Math.max(totalLength, totalLength + childWidth + lp.leftMargin +
+                           lp.rightMargin + getNextLocationOffset(child));
+                }
 
                 if (useLargestChild) {
                     largestChildWidth = Math.max(childWidth, largestChildWidth);
@@ -780,8 +794,14 @@
 
                 final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
                         child.getLayoutParams();
-                mTotalLength += largestChildWidth + lp.leftMargin + lp.rightMargin +
-                        getNextLocationOffset(child);
+                if (isExactly) {
+                    mTotalLength += largestChildWidth + lp.leftMargin + lp.rightMargin +
+                            getNextLocationOffset(child);
+                } else {
+                    final int totalLength = mTotalLength;
+                    mTotalLength = Math.max(totalLength, totalLength + largestChildWidth +
+                            lp.leftMargin + lp.rightMargin + getNextLocationOffset(child));
+                }
             }
         }
 
@@ -851,8 +871,14 @@
                     }
                 }
 
-                mTotalLength += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin +
-                        getNextLocationOffset(child);
+                if (isExactly) {
+                    mTotalLength += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin +
+                            getNextLocationOffset(child);
+                } else {
+                    final int totalLength = mTotalLength;
+                    mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredWidth() +
+                            lp.leftMargin + lp.rightMargin + getNextLocationOffset(child));
+                }
 
                 boolean matchHeightLocally = heightMode != MeasureSpec.EXACTLY &&
                         lp.height == LayoutParams.MATCH_PARENT;
diff --git a/core/java/com/android/internal/content/SyncStateContentProviderHelper.java b/core/java/com/android/internal/content/SyncStateContentProviderHelper.java
index cd6a9a1..274082c 100644
--- a/core/java/com/android/internal/content/SyncStateContentProviderHelper.java
+++ b/core/java/com/android/internal/content/SyncStateContentProviderHelper.java
@@ -50,6 +50,11 @@
 
     public static final String PATH = "syncstate";
 
+    private static final String QUERY_COUNT_SYNC_STATE_ROWS =
+            "SELECT count(*)"
+                    + " FROM " + SYNC_STATE_TABLE
+                    + " WHERE " + SyncStateContract.Columns._ID + "=?";
+
     public void createDatabase(SQLiteDatabase db) {
         db.execSQL("DROP TABLE IF EXISTS " + SYNC_STATE_TABLE);
         db.execSQL("CREATE TABLE " + SYNC_STATE_TABLE + " ("
@@ -96,11 +101,17 @@
         return db.update(SYNC_STATE_TABLE, values, selection, selectionArgs);
     }
 
-    public void update(SQLiteDatabase db, long rowId, Object data) {
+    public int update(SQLiteDatabase db, long rowId, Object data) {
+        if (DatabaseUtils.longForQuery(db, QUERY_COUNT_SYNC_STATE_ROWS,
+                new String[]{Long.toString(rowId)}) < 1) {
+            return 0;
+        }
         db.execSQL("UPDATE " + SYNC_STATE_TABLE
                 + " SET " + SyncStateContract.Columns.DATA + "=?"
                 + " WHERE " + SyncStateContract.Columns._ID + "=" + rowId,
                 new Object[]{data});
+        // assume a row was modified since we know it exists
+        return 1;
     }
 
     public void onAccountsChanged(SQLiteDatabase db, Account[] accounts) {
diff --git a/docs/html/guide/practices/design/performance.jd b/docs/html/guide/practices/design/performance.jd
index ab3b3d3..baed020 100644
--- a/docs/html/guide/practices/design/performance.jd
+++ b/docs/html/guide/practices/design/performance.jd
@@ -356,25 +356,19 @@
 
 <p>There are several alternatives for iterating through an array:</p>
 
-<pre>public class Foo {
-    int mSplat;
-}
-public class ArrayBenchmark {
-    Foo[] mArray = new Foo[27];
-    {
-        for (int i = 0; i &lt; mArray.length; ++i) {
-            mArray[i] = new Foo();
-        }
+<pre>    static class Foo {
+        int mSplat;
     }
+    Foo[] mArray = ...
 
-    public static void zero() {
+    public void zero() {
         int sum = 0;
         for (int i = 0; i &lt; mArray.length; ++i) {
             sum += mArray[i].mSplat;
         }
     }
 
-    public static void one() {
+    public void one() {
         int sum = 0;
         Foo[] localArray = mArray;
         int len = localArray.length;
@@ -384,13 +378,13 @@
         }
     }
 
-    public static void two() {
+    public void two() {
         int sum = 0;
         for (Foo a : mArray) {
             sum += a.mSplat;
         }
     }
-}</pre>
+</pre>
 
 <p><strong>zero()</strong> is slowest, because the JIT can't yet optimize away
 the cost of getting the array length once for every iteration through the
diff --git a/libs/audioflinger/AudioPolicyManagerBase.cpp b/libs/audioflinger/AudioPolicyManagerBase.cpp
index 42b6508..7b866c7 100644
--- a/libs/audioflinger/AudioPolicyManagerBase.cpp
+++ b/libs/audioflinger/AudioPolicyManagerBase.cpp
@@ -82,8 +82,8 @@
                     // keep track of SCO device address
                     mScoDeviceAddress = String8(device_address, MAX_DEVICE_ADDRESS_LEN);
 #ifdef WITH_A2DP
-                    if ((mA2dpDeviceAddress == mScoDeviceAddress) &&
-                        (mPhoneState != AudioSystem::MODE_NORMAL)) {
+                    if (mA2dpOutput != 0 &&
+                        mPhoneState != AudioSystem::MODE_NORMAL) {
                         mpClientInterface->suspendOutput(mA2dpOutput);
                     }
 #endif
@@ -116,8 +116,8 @@
                 if (AudioSystem::isBluetoothScoDevice(device)) {
                     mScoDeviceAddress = "";
 #ifdef WITH_A2DP
-                    if ((mA2dpDeviceAddress == mScoDeviceAddress) &&
-                        (mPhoneState != AudioSystem::MODE_NORMAL)) {
+                    if (mA2dpOutput != 0 &&
+                        mPhoneState != AudioSystem::MODE_NORMAL) {
                         mpClientInterface->restoreOutput(mA2dpOutput);
                     }
 #endif
@@ -275,10 +275,8 @@
     newDevice = getNewDevice(mHardwareOutput, false);
 #ifdef WITH_A2DP
     checkOutputForAllStrategies(newDevice);
-    // suspend A2DP output if SCO device address is the same as A2DP device address.
-    // no need to check that a SCO device is actually connected as mScoDeviceAddress == ""
-    // if none is connected and the test below will fail.
-    if (mA2dpDeviceAddress == mScoDeviceAddress) {
+    // suspend A2DP output if a SCO device is present.
+    if (mA2dpOutput != 0 && mScoDeviceAddress != "") {
         if (oldState == AudioSystem::MODE_NORMAL) {
             mpClientInterface->suspendOutput(mA2dpOutput);
         } else if (state == AudioSystem::MODE_NORMAL) {
@@ -1191,7 +1189,7 @@
     }
     AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput);
 
-    if (mA2dpDeviceAddress == mScoDeviceAddress) {
+    if (mScoDeviceAddress != "") {
         // It is normal to suspend twice if we are both in call,
         // and have the hardware audio output routed to BT SCO
         if (mPhoneState != AudioSystem::MODE_NORMAL) {
@@ -1556,9 +1554,9 @@
         usleep(outputDesc->mLatency*2*1000);
     }
 #ifdef WITH_A2DP
-    // suspend A2D output if SCO device is selected
+    // suspend A2DP output if SCO device is selected
     if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)device)) {
-         if (mA2dpOutput && mScoDeviceAddress == mA2dpDeviceAddress) {
+         if (mA2dpOutput != 0) {
              mpClientInterface->suspendOutput(mA2dpOutput);
          }
     }
@@ -1573,7 +1571,7 @@
 #ifdef WITH_A2DP
     // if disconnecting SCO device, restore A2DP output
     if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)prevDevice)) {
-         if (mA2dpOutput && mScoDeviceAddress == mA2dpDeviceAddress) {
+         if (mA2dpOutput != 0) {
              LOGV("restore A2DP output");
              mpClientInterface->restoreOutput(mA2dpOutput);
          }
diff --git a/packages/TtsService/jni/android_tts_SynthProxy.cpp b/packages/TtsService/jni/android_tts_SynthProxy.cpp
index 35b5675..2f5cfa3 100644
--- a/packages/TtsService/jni/android_tts_SynthProxy.cpp
+++ b/packages/TtsService/jni/android_tts_SynthProxy.cpp
@@ -45,6 +45,9 @@
 #define USAGEMODE_PLAY_IMMEDIATELY 0
 #define USAGEMODE_WRITE_TO_FILE    1
 
+#define SYNTHPLAYSTATE_IS_STOPPED 0
+#define SYNTHPLAYSTATE_IS_PLAYING 1
+
 using namespace android;
 
 // ----------------------------------------------------------------------------
@@ -154,6 +157,8 @@
         TtsEngine*                mNativeSynthInterface;
         void*                     mEngineLibHandle;
         AudioTrack*               mAudioOut;
+        int8_t                    mPlayState;
+        Mutex                     mPlayLock;
         AudioSystem::stream_type  mStreamType;
         uint32_t                  mSampleRate;
         uint32_t                  mAudFormat;
@@ -166,6 +171,7 @@
             mNativeSynthInterface = NULL;
             mEngineLibHandle = NULL;
             mAudioOut = NULL;
+            mPlayState =  SYNTHPLAYSTATE_IS_STOPPED;
             mStreamType = DEFAULT_TTS_STREAM_TYPE;
             mSampleRate = DEFAULT_TTS_RATE;
             mAudFormat  = DEFAULT_TTS_FORMAT;
@@ -223,6 +229,7 @@
             if (minBufCount < 2) minBufCount = 2;
             int minFrameCount = (afFrameCount * rate * minBufCount)/afSampleRate;
 
+            mPlayLock.lock();
             mAudioOut = new AudioTrack(mStreamType, rate, format,
                     (channel == 2) ? AudioSystem::CHANNEL_OUT_STEREO : AudioSystem::CHANNEL_OUT_MONO,
                     minFrameCount > 4096 ? minFrameCount : 4096,
@@ -237,6 +244,7 @@
               mAudioOut->setVolume(1.0f, 1.0f);
               LOGV("AudioTrack ready");
             }
+            mPlayLock.unlock();
         }
 };
 
@@ -288,6 +296,12 @@
         if (bufferSize > 0) {
             prepAudioTrack(pJniData, pForAfter->streamType, rate, (AudioSystem::audio_format)format, channel);
             if (pJniData->mAudioOut) {
+                pJniData->mPlayLock.lock();
+                if(pJniData->mAudioOut->stopped()
+                        && (pJniData->mPlayState == SYNTHPLAYSTATE_IS_PLAYING)) {
+                    pJniData->mAudioOut->start();
+                }
+                pJniData->mPlayLock.unlock();
                 if (bUseFilter) {
                     applyFilter((int16_t*)wav, bufferSize/2);
                 }
@@ -711,9 +725,9 @@
 
     SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
 
-    if (pSynthData->mAudioOut) {
-        pSynthData->mAudioOut->start();
-    }
+    pSynthData->mPlayLock.lock();
+    pSynthData->mPlayState = SYNTHPLAYSTATE_IS_PLAYING;
+    pSynthData->mPlayLock.unlock();
 
     afterSynthData_t* pForAfter = new (afterSynthData_t);
     pForAfter->jniStorage = jniData;
@@ -744,9 +758,13 @@
 
     SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
 
+    pSynthData->mPlayLock.lock();
+    pSynthData->mPlayState = SYNTHPLAYSTATE_IS_STOPPED;
     if (pSynthData->mAudioOut) {
         pSynthData->mAudioOut->stop();
     }
+    pSynthData->mPlayLock.unlock();
+
     if (pSynthData->mNativeSynthInterface) {
         result = pSynthData->mNativeSynthInterface->stop();
     }
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
index ac98054..6ceb0f9 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
@@ -81,6 +81,30 @@
     };
 
     static void fillIgnoreResultList() {
+        // This first block of tests are for HTML5 features, for which Android
+        // should pass all tests. They are skipped only temporarily.
+        // TODO: Fix these failing tests and remove them from this list.
+        ignoreResultList.add("fast/dom/Geolocation/callback-exception.html"); // exception output incorrect with V8
+        ignoreResultList.add("http/tests/appcache/auth.html"); // file not found
+        ignoreResultList.add("http/tests/appcache/deferred-events.html"); // file not found
+        ignoreResultList.add("http/tests/appcache/deferred-events-delete-while-raising.html"); // file not found
+        ignoreResultList.add("http/tests/appcache/destroyed-frame.html"); // file not found
+        ignoreResultList.add("http/tests/appcache/detached-iframe.html"); // file not found
+        ignoreResultList.add("http/tests/appcache/different-scheme.html"); // file not found
+        ignoreResultList.add("http/tests/appcache/disabled.html"); // not found
+        ignoreResultList.add("http/tests/appcache/empty-manifest.html"); // flaky
+        ignoreResultList.add("http/tests/appcache/foreign-iframe-main.html"); // flaky - skips states
+        ignoreResultList.add("http/tests/appcache/local-content.html"); // text diff
+        ignoreResultList.add("http/tests/appcache/max-size.html"); // no layoutTestController.setAppCacheMaximumSize
+        ignoreResultList.add("http/tests/appcache/manifest-with-empty-file.html"); // flaky
+        ignoreResultList.add("http/tests/appcache/whitelist-wildcard.html"); // file not found
+        ignoreResultList.add("storage/database-lock-after-reload.html"); // failure
+        ignoreResultList.add("storage/domstorage/localstorage/string-conversion.html"); // false failure due to whitespace diff in output with V8
+        ignoreResultList.add("storage/domstorage/sessionstorage/string-conversion.html"); // false failure due to whitespace diff in output with V8
+        ignoreResultList.add("storage/statement-error-callback.html"); // expected line number diff in V8 only
+        ignoreResultList.add("storage/transaction-error-callback.html"); // expected line number diff in V8 only
+        ignoreResultList.add("storage/transaction-callback-exception-crash.html"); // expected line number diff in V8 only
+
         ignoreResultList.add("fast/css/case-transform.html"); // will not fix #619707
         ignoreResultList.add("fast/dom/Element/offsetLeft-offsetTop-body-quirk.html"); // different screen size result in extra spaces in Apple compared to us
         ignoreResultList.add("fast/dom/Window/Plug-ins.html"); // need test plugin
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index f8c5c38..ae4bd14 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -224,9 +224,9 @@
                 ssize_t minSdkIndex = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
                                                              "minSdkVersion");
                 if (minSdkIndex >= 0) {
-                    String8 minSdkString = String8(
-                        block.getAttributeStringValue(minSdkIndex, &len));
-                    bundle->setMinSdkVersion(minSdkString.string());
+                    const uint16_t* minSdk16 = block.getAttributeStringValue(minSdkIndex, &len);
+                    const char* minSdk8 = strdup(String8(minSdk16).string());
+                    bundle->setMinSdkVersion(minSdk8);
                 }
             }
         }