Merge "Fix bug 2203203 Route STREAM_VOICE_CALL to A2DP when not in call."
diff --git a/core/java/android/app/SearchableInfo.java b/core/java/android/app/SearchableInfo.java
index 4496354..b8c54dd 100644
--- a/core/java/android/app/SearchableInfo.java
+++ b/core/java/android/app/SearchableInfo.java
@@ -303,7 +303,7 @@
                 InputType.TYPE_CLASS_TEXT |
                 InputType.TYPE_TEXT_VARIATION_NORMAL);
         mSearchImeOptions = a.getInt(com.android.internal.R.styleable.Searchable_imeOptions, 
-                EditorInfo.IME_ACTION_SEARCH);
+                EditorInfo.IME_ACTION_GO);
         mIncludeInGlobalSearch = a.getBoolean(
                 com.android.internal.R.styleable.Searchable_includeInGlobalSearch, false);
         mQueryAfterZeroResults = a.getBoolean(
@@ -672,7 +672,7 @@
     
     /**
      * Return the input method options specified in the searchable attributes.
-     * This will default to EditorInfo.ACTION_SEARCH if not specified (which is
+     * This will default to EditorInfo.ACTION_GO if not specified (which is
      * appropriate for a search box).
      * 
      * @return the input type
diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
index af54a71..1d154ce 100644
--- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java
+++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
@@ -40,7 +40,7 @@
 
     private Map<String, String> mProjectionMap = null;
     private String mTables = "";
-    private final StringBuilder mWhereClause = new StringBuilder(64);
+    private StringBuilder mWhereClause = null;  // lazily created
     private boolean mDistinct;
     private SQLiteDatabase.CursorFactory mFactory;
 
@@ -89,6 +89,9 @@
      * @param inWhere the chunk of text to append to the WHERE clause.
      */
     public void appendWhere(CharSequence inWhere) {
+        if (mWhereClause == null) {
+            mWhereClause = new StringBuilder(inWhere.length() + 16);
+        }
         if (mWhereClause.length() == 0) {
             mWhereClause.append('(');
         }
@@ -106,6 +109,9 @@
      * to avoid SQL injection attacks
      */
     public void appendWhereEscapeString(String inWhere) {
+        if (mWhereClause == null) {
+            mWhereClause = new StringBuilder(inWhere.length() + 16);
+        }
         if (mWhereClause.length() == 0) {
             mWhereClause.append('(');
         }
@@ -356,15 +362,16 @@
         String[] projection = computeProjection(projectionIn);
 
         StringBuilder where = new StringBuilder();
+        boolean hasBaseWhereClause = mWhereClause != null && mWhereClause.length() > 0;
 
-        if (mWhereClause.length() > 0) {
+        if (hasBaseWhereClause) {
             where.append(mWhereClause.toString());
             where.append(')');
         }
 
         // Tack on the user's selection, if present.
         if (selection != null && selection.length() > 0) {
-            if (mWhereClause.length() > 0) {
+            if (hasBaseWhereClause) {
                 where.append(" AND ");
             }
 
diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java
index 4377a2b..27092ae 100644
--- a/core/java/android/provider/Browser.java
+++ b/core/java/android/provider/Browser.java
@@ -351,23 +351,27 @@
      *  @hide pending API council approval
      */
     public static final String[] getVisitedHistory(ContentResolver cr) {
+        Cursor c = null;
+        String[] str = null;
         try {
             String[] projection = new String[] {
                 "url"
             };
-            Cursor c = cr.query(BOOKMARKS_URI, projection, "visits > 0", null,
+            c = cr.query(BOOKMARKS_URI, projection, "visits > 0", null,
                     null);
-            String[] str = new String[c.getCount()];
+            str = new String[c.getCount()];
             int i = 0;
             while (c.moveToNext()) {
                 str[i] = c.getString(0);
                 i++;
             }
-            c.close();
-            return str;
         } catch (IllegalStateException e) {
-            return new String[0];
+            Log.e(LOGTAG, "getVisitedHistory", e);
+            str = new String[0];
+        } finally {
+            if (c != null) c.close();
         }
+        return str;
     }
 
     /**
@@ -382,9 +386,10 @@
      * @param cr The ContentResolver used to access the database.
      */
     public static final void truncateHistory(ContentResolver cr) {
+        Cursor c = null;
         try {
             // Select non-bookmark history, ordered by date
-            Cursor c = cr.query(
+            c = cr.query(
                     BOOKMARKS_URI,
                     TRUNCATE_HISTORY_PROJECTION,
                     "bookmark = 0",
@@ -396,16 +401,16 @@
                 for (int i = 0; i < TRUNCATE_N_OLDEST; i++) {
                     // Log.v(LOGTAG, "truncate history " +
                     // c.getInt(TRUNCATE_HISTORY_PROJECTION_ID_INDEX));
-                    deleteHistoryWhere(
-                            cr, "_id = " +
-                            c.getInt(TRUNCATE_HISTORY_PROJECTION_ID_INDEX));
+                    cr.delete(BOOKMARKS_URI, "_id = " +
+                            c.getInt(TRUNCATE_HISTORY_PROJECTION_ID_INDEX),
+                            null);
                     if (!c.moveToNext()) break;
                 }
             }
-            c.close();
         } catch (IllegalStateException e) {
             Log.e(LOGTAG, "truncateHistory", e);
-            return;
+        } finally {
+            if (c != null) c.close();
         }
     }
 
@@ -416,8 +421,10 @@
      * @return boolean  True if the history can be cleared.
      */
     public static final boolean canClearHistory(ContentResolver cr) {
+        Cursor c = null;
+        boolean ret = false;
         try {
-            Cursor c = cr.query(
+            c = cr.query(
                 BOOKMARKS_URI,
                 new String [] { BookmarkColumns._ID, 
                                 BookmarkColumns.BOOKMARK,
@@ -426,12 +433,13 @@
                 null,
                 null
                 );
-            boolean ret = c.moveToFirst();
-            c.close();
-            return ret;
+            ret = c.moveToFirst();
         } catch (IllegalStateException e) {
-            return false;
+            Log.e(LOGTAG, "canClearHistory", e);
+        } finally {
+            if (c != null) c.close();
         }
+        return ret;
     }
 
     /**
@@ -455,57 +463,57 @@
      */
     private static final void deleteHistoryWhere(ContentResolver cr,
             String whereClause) {
+        Cursor c = null;
         try {
-            Cursor c = cr.query(BOOKMARKS_URI,
+            c = cr.query(BOOKMARKS_URI,
                 HISTORY_PROJECTION,
                 whereClause,
                 null,
                 null);
-            if (!c.moveToFirst()) {
-                c.close();
-                return;
-            }
-
-            final WebIconDatabase iconDb = WebIconDatabase.getInstance();
-            /* Delete favicons, and revert bookmarks which have been visited
-             * to simply bookmarks.
-             */
-            StringBuffer sb = new StringBuffer();
-            boolean firstTime = true;
-            do {
-                String url = c.getString(HISTORY_PROJECTION_URL_INDEX);
-                boolean isBookmark = 
-                    c.getInt(HISTORY_PROJECTION_BOOKMARK_INDEX) == 1;
-                if (isBookmark) {
-                    if (firstTime) {
-                        firstTime = false;
+            if (c.moveToFirst()) {
+                final WebIconDatabase iconDb = WebIconDatabase.getInstance();
+                /* Delete favicons, and revert bookmarks which have been visited
+                 * to simply bookmarks.
+                 */
+                StringBuffer sb = new StringBuffer();
+                boolean firstTime = true;
+                do {
+                    String url = c.getString(HISTORY_PROJECTION_URL_INDEX);
+                    boolean isBookmark =
+                        c.getInt(HISTORY_PROJECTION_BOOKMARK_INDEX) == 1;
+                    if (isBookmark) {
+                        if (firstTime) {
+                            firstTime = false;
+                        } else {
+                            sb.append(" OR ");
+                        }
+                        sb.append("( _id = ");
+                        sb.append(c.getInt(0));
+                        sb.append(" )");
                     } else {
-                        sb.append(" OR ");
+                        iconDb.releaseIconForPageUrl(url);
                     }
-                    sb.append("( _id = ");
-                    sb.append(c.getInt(0));
-                    sb.append(" )");
-                } else {
-                    iconDb.releaseIconForPageUrl(url);
+                } while (c.moveToNext());
+
+                if (!firstTime) {
+                    ContentValues map = new ContentValues();
+                    map.put(BookmarkColumns.VISITS, 0);
+                    map.put(BookmarkColumns.DATE, 0);
+                    /* FIXME: Should I also remove the title? */
+                    cr.update(BOOKMARKS_URI, map, sb.toString(), null);
                 }
-            } while (c.moveToNext());
-            c.close();
 
-            if (!firstTime) {
-                ContentValues map = new ContentValues();
-                map.put(BookmarkColumns.VISITS, 0);
-                map.put(BookmarkColumns.DATE, 0);
-                /* FIXME: Should I also remove the title? */
-                cr.update(BOOKMARKS_URI, map, sb.toString(), null);
+                String deleteWhereClause = BookmarkColumns.BOOKMARK + " = 0";
+                if (whereClause != null) {
+                    deleteWhereClause += " AND " + whereClause;
+                }
+                cr.delete(BOOKMARKS_URI, deleteWhereClause, null);
             }
-
-            String deleteWhereClause = BookmarkColumns.BOOKMARK + " = 0";
-            if (whereClause != null) {
-                deleteWhereClause += " AND " + whereClause;
-            }
-            cr.delete(BOOKMARKS_URI, deleteWhereClause, null);
         } catch (IllegalStateException e) {
+            Log.e(LOGTAG, "deleteHistoryWhere", e);
             return;
+        } finally {
+            if (c != null) c.close();
         }
     }
 
@@ -560,8 +568,9 @@
      */
     public static final void addSearchUrl(ContentResolver cr, String search) {
         long now = new Date().getTime();
+        Cursor c = null;
         try {
-            Cursor c = cr.query(
+            c = cr.query(
                 SEARCHES_URI,
                 SEARCHES_PROJECTION,
                 SEARCHES_WHERE_CLAUSE,
@@ -576,10 +585,10 @@
             } else {
                 cr.insert(SEARCHES_URI, map);
             }
-            c.close();
         } catch (IllegalStateException e) {
             Log.e(LOGTAG, "addSearchUrl", e);
-            return;
+        } finally {
+            if (c != null) c.close();
         }
     }
     /**
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 582ef3f..484922d 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -2241,7 +2241,8 @@
      *   leaving touch mode alone is considered the event).
      */
     private boolean checkForLeavingTouchModeAndConsume(KeyEvent event) {
-        if (event.getAction() != KeyEvent.ACTION_DOWN) {
+        final int action = event.getAction();
+        if (action != KeyEvent.ACTION_DOWN && action != KeyEvent.ACTION_MULTIPLE) {
             return false;
         }
         if ((event.getFlags()&KeyEvent.FLAG_KEEP_TOUCH_MODE) != 0) {
diff --git a/core/java/android/webkit/PluginFullScreenHolder.java b/core/java/android/webkit/PluginFullScreenHolder.java
index b641803..6d9e108 100644
--- a/core/java/android/webkit/PluginFullScreenHolder.java
+++ b/core/java/android/webkit/PluginFullScreenHolder.java
@@ -25,8 +25,6 @@
 package android.webkit;
 
 import android.app.Dialog;
-import android.graphics.Rect;
-import android.util.Log;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
@@ -34,15 +32,9 @@
 
 class PluginFullScreenHolder extends Dialog {
 
-    private static final String LOGTAG = "FullScreenHolder";
-
     private final WebView mWebView;
     private final int mNpp;
     private View mContentView;
-    private int mX;
-    private int mY;
-    private int mWidth;
-    private int mHeight;
 
     PluginFullScreenHolder(WebView webView, int npp) {
         super(webView.getContext(), android.R.style.Theme_NoTitleBar_Fullscreen);
@@ -50,23 +42,15 @@
         mNpp = npp;
     }
 
-    Rect getBound() {
-        return new Rect(mX, mY, mWidth, mHeight);
-    }
-
-    /*
-     * x, y, width, height are in the caller's view coordinate system. (x, y) is
-     * relative to the top left corner of the caller's view.
-     */
-    void updateBound(int x, int y, int width, int height) {
-        mX = x;
-        mY = y;
-        mWidth = width;
-        mHeight = height;
-    }
-
     @Override
     public void setContentView(View contentView) {
+        // as we are sharing the View between full screen and
+        // embedded mode, we have to remove the
+        // AbsoluteLayout.LayoutParams set by embedded mode to
+        // ViewGroup.LayoutParams before adding it to the dialog
+        contentView.setLayoutParams(new ViewGroup.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT));
         super.setContentView(contentView);
         mContentView = contentView;
     }
@@ -99,15 +83,7 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
-        final float x = event.getX();
-        final float y = event.getY();
-        // TODO: find a way to know when the dialog size changed so that we can
-        // cache the ratio
-        final View decorView = getWindow().getDecorView();
-        event.setLocation(mX + x * mWidth / decorView.getWidth(),
-                mY + y * mHeight / decorView.getHeight());
-        mWebView.onTouchEvent(event);
-        // always return true as we are the handler
+        // always return true as we don't want the event to propagate any further
         return true;
     }
 
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index c5a4e9e..52bc8be1 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1464,7 +1464,9 @@
      * Load the given data into the WebView. This will load the data into
      * WebView using the data: scheme. Content loaded through this mechanism
      * does not have the ability to load content from the network.
-     * @param data A String of data in the given encoding.
+     * @param data A String of data in the given encoding. The date must
+     * be URI-escaped -- '#', '%', '\', '?' should be replaced by %23, %25,
+     * %27, %3f respectively.
      * @param mimeType The MIMEType of the data. i.e. text/html, image/jpeg
      * @param encoding The encoding of the data. i.e. utf-8, base64
      */
@@ -6508,80 +6510,21 @@
                     break;
 
                 case SHOW_FULLSCREEN: {
-                    WebViewCore.PluginFullScreenData data
-                            = (WebViewCore.PluginFullScreenData) msg.obj;
-                    if (data.mNpp != 0 && data.mView != null) {
-                        if (inFullScreenMode()) {
-                            Log.w(LOGTAG,
-                                    "Should not have another full screen.");
-                            mFullScreenHolder.dismiss();
-                        }
-                        mFullScreenHolder = new PluginFullScreenHolder(
-                                WebView.this, data.mNpp);
-                        // as we are sharing the View between full screen and
-                        // embedded mode, we have to remove the
-                        // AbsoluteLayout.LayoutParams set by embedded mode to
-                        // ViewGroup.LayoutParams before adding it to the dialog
-                        data.mView.setLayoutParams(new FrameLayout.LayoutParams(
-                                ViewGroup.LayoutParams.FILL_PARENT,
-                                ViewGroup.LayoutParams.FILL_PARENT));
-                        mFullScreenHolder.setContentView(data.mView);
-                        mFullScreenHolder.setCancelable(false);
-                        mFullScreenHolder.setCanceledOnTouchOutside(false);
-                        mFullScreenHolder.show();
-                    } else if (!inFullScreenMode()) {
-                        // this may happen if user dismisses the fullscreen and
-                        // then the WebCore re-position message finally reached
-                        // the UI thread.
-                        break;
-                    }
-                    // move the matching embedded view fully into the view so
-                    // that touch will be valid instead of rejected due to out
-                    // of the visible bounds
-                    // TODO: do we need to preserve the original position and
-                    // scale so that we can revert it when leaving the full
-                    // screen mode?
-                    int x = contentToViewX(data.mDocX);
-                    int y = contentToViewY(data.mDocY);
-                    int width = contentToViewDimension(data.mDocWidth);
-                    int height = contentToViewDimension(data.mDocHeight);
-                    int viewWidth = getViewWidth();
-                    int viewHeight = getViewHeight();
-                    int newX = mScrollX;
-                    int newY = mScrollY;
-                    if (x < mScrollX) {
-                        newX = x + (width > viewWidth
-                                ? (width - viewWidth) / 2 : 0);
-                    } else if (x + width > mScrollX + viewWidth) {
-                        newX = x + width - viewWidth - (width > viewWidth
-                                ? (width - viewWidth) / 2 : 0);
-                    }
-                    if (y < mScrollY) {
-                        newY = y + (height > viewHeight
-                                ? (height - viewHeight) / 2 : 0);
-                    } else if (y + height > mScrollY + viewHeight) {
-                        newY = y + height - viewHeight - (height > viewHeight
-                                ? (height - viewHeight) / 2 : 0);
-                    }
-                    scrollTo(newX, newY);
-                    if (width > viewWidth || height > viewHeight) {
-                        mZoomCenterX = viewWidth * .5f;
-                        mZoomCenterY = viewHeight * .5f;
-                        // do not change text wrap scale so that there is no
-                        // reflow
-                        setNewZoomScale(mActualScale
-                                / Math.max((float) width / viewWidth,
-                                        (float) height / viewHeight), false,
-                                false);
-                    }
-                    // Now update the bound
-                    mFullScreenHolder.updateBound(contentToViewX(data.mDocX)
-                            - mScrollX, contentToViewY(data.mDocY) - mScrollY,
-                            contentToViewDimension(data.mDocWidth),
-                            contentToViewDimension(data.mDocHeight));
-                    }
-                    break;
+                    View view = (View) msg.obj;
+                    int npp = msg.arg1;
 
+                    if (mFullScreenHolder != null) {
+                        Log.w(LOGTAG, "Should not have another full screen.");
+                        mFullScreenHolder.dismiss();
+                    }
+                    mFullScreenHolder = new PluginFullScreenHolder(WebView.this, npp);
+                    mFullScreenHolder.setContentView(view);
+                    mFullScreenHolder.setCancelable(false);
+                    mFullScreenHolder.setCanceledOnTouchOutside(false);
+                    mFullScreenHolder.show();
+
+                    break;
+                }
                 case HIDE_FULLSCREEN:
                     if (inFullScreenMode()) {
                         mFullScreenHolder.dismiss();
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index a274378..4073e37 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -716,14 +716,7 @@
         boolean mRemember;
     }
 
-    static class PluginFullScreenData {
-        View mView;
-        int mNpp;
-        int mDocX;
-        int mDocY;
-        int mDocWidth;
-        int mDocHeight;
-    }
+
 
         static final String[] HandlerDebugString = {
             "REQUEST_LABEL", // 97
@@ -2351,22 +2344,15 @@
 
     // called by JNI. PluginWidget function to launch a full-screen view using a
     // View object provided by the plugin class.
-    private void showFullScreenPlugin(ViewManager.ChildView childView,
-            final int npp, int x, int y, int width, int height) {
-
+    private void showFullScreenPlugin(ViewManager.ChildView childView, int npp) {
         if (mWebView == null) {
             return;
         }
 
-        PluginFullScreenData data = new PluginFullScreenData();
-        data.mView = childView.mView;
-        data.mNpp = npp;
-        data.mDocX = x;
-        data.mDocY = y;
-        data.mDocWidth = width;
-        data.mDocHeight = height;
-        mWebView.mPrivateHandler.obtainMessage(WebView.SHOW_FULLSCREEN, data)
-                .sendToTarget();
+        Message message = mWebView.mPrivateHandler.obtainMessage(WebView.SHOW_FULLSCREEN);
+        message.obj = childView.mView;
+        message.arg1 = npp;
+        message.sendToTarget();
     }
 
     // called by JNI
@@ -2378,22 +2364,6 @@
                 .sendToTarget();
     }
 
-    // called by JNI
-    private void updateFullScreenPlugin(int x, int y, int width, int height) {
-        if (mWebView == null) {
-            return;
-        }
-
-        PluginFullScreenData data = new PluginFullScreenData();
-        data.mDocX = x;
-        data.mDocY = y;
-        data.mDocWidth = width;
-        data.mDocHeight = height;
-        // null mView and mNpp to indicate it is an update
-        mWebView.mPrivateHandler.obtainMessage(WebView.SHOW_FULLSCREEN, data)
-                .sendToTarget();
-    }
-
     // called by JNI.  PluginWidget functions for creating an embedded View for
     // the surface drawing model.
     private ViewManager.ChildView addSurface(View pluginView, int x, int y,
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 5bded0b..a07397f 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -1980,11 +1980,11 @@
             if (touchMode == TOUCH_MODE_OVERSCROLL || touchMode == TOUCH_MODE_OVERFLING) {
                 if (mFlingRunnable != null) {
                     mFlingRunnable.endFling();
-                    
-                    if (mScrollY != 0) {
-                        mScrollY = 0;
-                        invalidate();
-                    }
+                }
+
+                if (mScrollY != 0) {
+                    mScrollY = 0;
+                    invalidate();
                 }
             }
         }
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index d7485ae..17f5daf 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -55,6 +55,8 @@
     jobject     audioRecord_ref;
  };
 
+Mutex sLock;
+
 // ----------------------------------------------------------------------------
 
 #define AUDIORECORD_SUCCESS                         0
@@ -255,12 +257,21 @@
 
 
 // ----------------------------------------------------------------------------
-static void android_media_AudioRecord_finalize(JNIEnv *env,  jobject thiz) {
-    
-    // delete the AudioRecord object
+static void android_media_AudioRecord_release(JNIEnv *env,  jobject thiz) {
+
+    // serialize access. Ugly, but functional.
+    Mutex::Autolock lock(&sLock);
     AudioRecord *lpRecorder = 
             (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
+    audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetIntField(
+        thiz, javaAudioRecordFields.nativeCallbackCookie);
 
+    // reset the native resources in the Java object so any attempt to access
+    // them after a call to release fails.
+    env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, 0);
+    env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
+
+    // delete the AudioRecord object
     if (lpRecorder) {
         LOGV("About to delete lpRecorder: %x\n", (int)lpRecorder);
         lpRecorder->stop();
@@ -268,27 +279,18 @@
     }
     
     // delete the callback information
-    audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetIntField(
-        thiz, javaAudioRecordFields.nativeCallbackCookie);
     if (lpCookie) {
         LOGV("deleting lpCookie: %x\n", (int)lpCookie);
         env->DeleteGlobalRef(lpCookie->audioRecord_class);
         env->DeleteGlobalRef(lpCookie->audioRecord_ref);
         delete lpCookie;
     }
-
 }
 
 
 // ----------------------------------------------------------------------------
-static void android_media_AudioRecord_release(JNIEnv *env,  jobject thiz) {
-       
-    // do everything a call to finalize would
-    android_media_AudioRecord_finalize(env, thiz);
-    // + reset the native resources in the Java object so any attempt to access
-    // them after a call to release fails.
-    env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, 0);
-    env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
+static void android_media_AudioRecord_finalize(JNIEnv *env,  jobject thiz) {
+    android_media_AudioRecord_release(env, thiz);
 }
 
 
diff --git a/data/fonts/DroidSansFallback.ttf b/data/fonts/DroidSansFallback.ttf
index a935f16..206621f 100644
--- a/data/fonts/DroidSansFallback.ttf
+++ b/data/fonts/DroidSansFallback.ttf
Binary files differ
diff --git a/include/media/MediaMetadataRetrieverInterface.h b/include/media/MediaMetadataRetrieverInterface.h
index e228357..ff57774 100644
--- a/include/media/MediaMetadataRetrieverInterface.h
+++ b/include/media/MediaMetadataRetrieverInterface.h
@@ -43,6 +43,10 @@
 class MediaMetadataRetrieverInterface : public MediaMetadataRetrieverBase
 {
 public:
+    MediaMetadataRetrieverInterface()
+        : mMode(0) {
+    }
+
     virtual             ~MediaMetadataRetrieverInterface() {}
 
     // @param mode The intended mode of operations:
@@ -57,6 +61,8 @@
                                 mode > METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL) {
                                 return BAD_VALUE;
                             }
+
+                            mMode = mode;
                             return NO_ERROR;
                         }
 
diff --git a/include/media/mediametadataretriever.h b/include/media/mediametadataretriever.h
index 9b12410..dbbcc49 100644
--- a/include/media/mediametadataretriever.h
+++ b/include/media/mediametadataretriever.h
@@ -67,8 +67,8 @@
 //   and meta data retrieval.$
 enum {
     METADATA_MODE_NOOP                                 = 0x00,
-    METADATA_MODE_FRAME_CAPTURE_ONLY                   = 0x01,
-    METADATA_MODE_METADATA_RETRIEVAL_ONLY              = 0x02,
+    METADATA_MODE_METADATA_RETRIEVAL_ONLY              = 0x01,
+    METADATA_MODE_FRAME_CAPTURE_ONLY                   = 0x02,
     METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL = 0x03
 };
 
diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java
index 23f850a..5062e87 100644
--- a/media/java/android/media/ThumbnailUtils.java
+++ b/media/java/android/media/ThumbnailUtils.java
@@ -568,7 +568,7 @@
         fullThumbWidth = fullOptions.outWidth / fullOptions.inSampleSize;
 
         // Choose the larger thumbnail as the returning sizedThumbBitmap.
-        if (exifThumbWidth >= fullThumbWidth) {
+        if (thumbData != null && exifThumbWidth >= fullThumbWidth) {
             int width = exifOptions.outWidth;
             int height = exifOptions.outHeight;
             exifOptions.inJustDecodeBounds = false;
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index 2012b3d..fef880b 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -144,7 +144,7 @@
 
 bool SoundPool::startThreads()
 {
-    createThreadEtc(beginThread, this, "SoundPoolThread");
+    createThreadEtc(beginThread, this, "SoundPool");
     if (mDecodeThread == NULL)
         mDecodeThread = new SoundPoolThread(this);
     return mDecodeThread != NULL;
@@ -827,4 +827,3 @@
 }
 
 } // end namespace android
-
diff --git a/media/jni/soundpool/SoundPoolThread.cpp b/media/jni/soundpool/SoundPoolThread.cpp
index e32c794..275c519 100644
--- a/media/jni/soundpool/SoundPoolThread.cpp
+++ b/media/jni/soundpool/SoundPoolThread.cpp
@@ -62,7 +62,7 @@
     mSoundPool(soundPool)
 {
     mMsgQueue.setCapacity(maxMessages);
-    if (createThread(beginThread, this)) {
+    if (createThreadEtc(beginThread, this, "SoundPoolThread")) {
         mRunning = true;
     }
 }
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index f1c3f0f..f50451d 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -501,7 +501,10 @@
                 }
             }
 
+            bool isTrack = false;
             if (chunk_type == FOURCC('t', 'r', 'a', 'k')) {
+                isTrack = true;
+
                 Track *track = new Track;
                 track->next = NULL;
                 if (mLastTrack) {
@@ -513,6 +516,7 @@
 
                 track->meta = new MetaData;
                 track->includes_expensive_metadata = false;
+                track->skipTrack = false;
                 track->timescale = 0;
                 track->sampleTable = new SampleTable(mDataSource);
                 track->meta->setCString(kKeyMIMEType, "application/octet-stream");
@@ -531,7 +535,25 @@
                 return ERROR_MALFORMED;
             }
 
-            if (chunk_type == FOURCC('t', 'r', 'a', 'k')) {
+            if (isTrack) {
+                if (mLastTrack->skipTrack) {
+                    Track *cur = mFirstTrack;
+
+                    if (cur == mLastTrack) {
+                        delete cur;
+                        mFirstTrack = mLastTrack = NULL;
+                    } else {
+                        while (cur && cur->next != mLastTrack) {
+                            cur = cur->next;
+                        }
+                        cur->next = NULL;
+                        delete mLastTrack;
+                        mLastTrack = cur;
+                    }
+
+                    return OK;
+                }
+
                 status_t err = verifyTrack(mLastTrack);
 
                 if (err != OK) {
@@ -655,32 +677,6 @@
             break;
         }
 
-        case FOURCC('h', 'd', 'l', 'r'):
-        {
-            if (chunk_data_size < 25) {
-                return ERROR_MALFORMED;
-            }
-
-            uint8_t buffer[24];
-            if (mDataSource->readAt(data_offset, buffer, 24) < 24) {
-                return ERROR_IO;
-            }
-
-            if (U32_AT(buffer) != 0) {
-                // Should be version 0, flags 0.
-                return ERROR_MALFORMED;
-            }
-
-            if (U32_AT(&buffer[4]) != 0) {
-                // pre_defined should be 0.
-                return ERROR_MALFORMED;
-            }
-
-            mHandlerType = U32_AT(&buffer[8]);
-            *offset += chunk_size;
-            break;
-        }
-
         case FOURCC('s', 't', 's', 'd'):
         {
             if (chunk_data_size < 8) {
@@ -706,7 +702,10 @@
 
             if (entry_count > 1) {
                 // For now we only support a single type of media per track.
-                return ERROR_UNSUPPORTED;
+
+                mLastTrack->skipTrack = true;
+                *offset += chunk_size;
+                break;
             }
 
             off_t stop_offset = *offset + chunk_size;
@@ -728,10 +727,6 @@
         case FOURCC('s', 'a', 'm', 'r'):
         case FOURCC('s', 'a', 'w', 'b'):
         {
-            if (mHandlerType != FOURCC('s', 'o', 'u', 'n')) {
-                return ERROR_MALFORMED;
-            }
-
             uint8_t buffer[8 + 20];
             if (chunk_data_size < (ssize_t)sizeof(buffer)) {
                 // Basic AudioSampleEntry size.
@@ -785,10 +780,6 @@
         {
             mHasVideo = true;
 
-            if (mHandlerType != FOURCC('v', 'i', 'd', 'e')) {
-                return ERROR_MALFORMED;
-            }
-
             uint8_t buffer[78];
             if (chunk_data_size < (ssize_t)sizeof(buffer)) {
                 // Basic VideoSampleEntry size.
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index 4679207..2968917 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -206,6 +206,12 @@
 VideoFrame *StagefrightMetadataRetriever::captureFrame() {
     LOGV("captureFrame");
 
+    if (0 == (mMode & METADATA_MODE_FRAME_CAPTURE_ONLY)) {
+        LOGV("captureFrame disabled by mode (0x%08x)", mMode);
+
+        return NULL;
+    }
+
     if (mExtractor.get() == NULL) {
         LOGV("no extractor.");
         return NULL;
@@ -256,6 +262,12 @@
 MediaAlbumArt *StagefrightMetadataRetriever::extractAlbumArt() {
     LOGV("extractAlbumArt (extractor: %s)", mExtractor.get() != NULL ? "YES" : "NO");
 
+    if (0 == (mMode & METADATA_MODE_METADATA_RETRIEVAL_ONLY)) {
+        LOGV("extractAlbumArt/metadata retrieval disabled by mode");
+
+        return NULL;
+    }
+
     if (mExtractor == NULL) {
         return NULL;
     }
@@ -274,6 +286,12 @@
 }
 
 const char *StagefrightMetadataRetriever::extractMetadata(int keyCode) {
+    if (0 == (mMode & METADATA_MODE_METADATA_RETRIEVAL_ONLY)) {
+        LOGV("extractAlbumArt/metadata retrieval disabled by mode");
+
+        return NULL;
+    }
+
     if (mExtractor == NULL) {
         return NULL;
     }
diff --git a/media/libstagefright/id3/Android.mk b/media/libstagefright/id3/Android.mk
index add8f3c..93fac1c 100644
--- a/media/libstagefright/id3/Android.mk
+++ b/media/libstagefright/id3/Android.mk
@@ -23,6 +23,8 @@
 LOCAL_STATIC_LIBRARIES := \
         libstagefright_id3
 
+LOCAL_MODULE_TAGS := debug
+
 LOCAL_MODULE := testid3
 
 include $(BUILD_EXECUTABLE)
diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
index 9d35e0c..c8663d5 100644
--- a/media/libstagefright/include/MPEG4Extractor.h
+++ b/media/libstagefright/include/MPEG4Extractor.h
@@ -48,6 +48,7 @@
         uint32_t timescale;
         sp<SampleTable> sampleTable;
         bool includes_expensive_metadata;
+        bool skipTrack;
     };
 
     sp<DataSource> mDataSource;
@@ -58,7 +59,6 @@
 
     sp<MetaData> mFileMetaData;
 
-    uint32_t mHandlerType;
     Vector<uint32_t> mPath;
 
     status_t readMetaData();
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index 13cb460..132e31b 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -19,7 +19,8 @@
 #include <utils/Log.h>
 
 #include <dlfcn.h>
-#include <linux/prctl.h>
+
+#include <sys/prctl.h>
 #include <sys/resource.h>
 
 #include "../include/OMX.h"
diff --git a/services/java/com/android/server/UiModeManagerService.java b/services/java/com/android/server/UiModeManagerService.java
index 84790ca..37a3cdf 100644
--- a/services/java/com/android/server/UiModeManagerService.java
+++ b/services/java/com/android/server/UiModeManagerService.java
@@ -423,6 +423,7 @@
             String action = null;
             String oldAction = null;
             if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_CAR) {
+                adjustStatusBarCarModeLocked();
                 oldAction = UiModeManager.ACTION_EXIT_CAR_MODE;
             } else if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_DESK) {
                 oldAction = UiModeManager.ACTION_EXIT_DESK_MODE;
@@ -447,10 +448,6 @@
                     action = UiModeManager.ACTION_ENTER_DESK_MODE;
                 }
             } else {
-                if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_CAR) {
-                    adjustStatusBarCarModeLocked();
-                }
-
                 mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
                 action = oldAction;
             }
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
index e3486a0..297b963 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
@@ -72,10 +72,10 @@
     };
 
     static final String[] ignoreTestList = {
+        "editing/selection/move-left-right.html", // Causes DumpRenderTree to hang
         "fast/js/regexp-charclass-crash.html", // RegExp is too large, causing OOM
         "fast/regex/test1.html", // Causes DumpRenderTree to hang with V8
-        "fast/regex/slow.html", // Causes DumpRenderTree to hang with V8
-        "editing/selection/move-left-right.html" // Causes DumpRenderTree to hang
+        "fast/regex/slow.html" // Causes DumpRenderTree to hang with V8
     };
 
     static void fillIgnoreResultList() {
@@ -98,6 +98,7 @@
         ignoreResultList.add("storage/database-lock-after-reload.html"); // failure
 
         // Will always fail
+        ignoreResultList.add("dom/svg/level3/xpath"); // XPath not supported
         ignoreResultList.add("fast/workers/shared-worker-constructor.html"); // shared workers not supported
         ignoreResultList.add("fast/workers/shared-worker-context-gc.html"); // shared workers not supported
         ignoreResultList.add("fast/workers/shared-worker-event-listener.html"); // shared workers not supported
@@ -114,6 +115,7 @@
         ignoreResultList.add("fast/workers/shared-worker-script-error.html"); // shared workers not supported
         ignoreResultList.add("fast/workers/shared-worker-shared.html"); // shared workers not supported
         ignoreResultList.add("fast/workers/shared-worker-simple.html"); // shared workers not supported
+        ignoreResultList.add("fast/xpath"); // XPath not supported
 
         // TODO: These need to be triaged
         ignoreResultList.add("fast/css/case-transform.html"); // will not fix #619707