Merge "Add new Slog class."
diff --git a/api/current.xml b/api/current.xml
index 3b90f38..89af2ac 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -189783,7 +189783,7 @@
  synchronized="false"
  static="true"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </method>
@@ -189835,7 +189835,7 @@
  synchronized="false"
  static="true"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </method>
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index 393bbba..211a2ae 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -819,7 +819,7 @@
             }
             scheduleSyncOperation(new SyncOperation(operation.account, operation.syncSource,
                     operation.authority, operation.extras,
-                    DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS));
+                    DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS * 1000));
         } else if (syncResult.hasSoftError()) {
             if (isLoggable) {
                 Log.d(TAG, "retrying sync operation because it encountered a soft error: "
diff --git a/core/java/android/content/SyncOperation.java b/core/java/android/content/SyncOperation.java
index 4599165..3b3f9c1 100644
--- a/core/java/android/content/SyncOperation.java
+++ b/core/java/android/content/SyncOperation.java
@@ -19,7 +19,7 @@
     public SyncStorageEngine.PendingOperation pendingOperation;
 
     public SyncOperation(Account account, int source, String authority, Bundle extras,
-            long delay) {
+            long delayInMs) {
         this.account = account;
         this.syncSource = source;
         this.authority = authority;
@@ -33,12 +33,12 @@
         removeFalseExtra(ContentResolver.SYNC_EXTRAS_EXPEDITED);
         removeFalseExtra(ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS);
         final long now = SystemClock.elapsedRealtime();
-        if (delay < 0) {
+        if (delayInMs < 0) {
             this.expedited = true;
             this.earliestRunTime = now;
         } else {
             this.expedited = false;
-            this.earliestRunTime = now + delay;
+            this.earliestRunTime = now + delayInMs;
         }
         this.key = toKey();
     }
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index d96596295..0de1868 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -101,6 +101,12 @@
         public static final String READ = "read";
 
         /**
+         * Indicates whether this message has been seen by the user. The "seen" flag will be
+         * used to figure out whether we need to throw up a statusbar notification or not.
+         */
+        public static final String SEEN = "seen";
+
+        /**
          * The TP-Status value for the message, or -1 if no status has
          * been received
          */
@@ -646,6 +652,12 @@
         public static final String READ = "read";
 
         /**
+         * Indicates whether this message has been seen by the user. The "seen" flag will be
+         * used to figure out whether we need to throw up a statusbar notification or not.
+         */
+        public static final String SEEN = "seen";
+
+        /**
          * The Message-ID of the message.
          * <P>Type: TEXT</P>
          */
@@ -1097,6 +1109,7 @@
          * <P>Type: INTEGER</P>
          */
         public static final String READ = "read";
+
         /**
          * The snippet of the latest message in the thread.
          * <P>Type: TEXT</P>
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index afb22ac..9589bf3 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -1557,13 +1557,17 @@
      * @param reqModes The modes to be checked: may be any combination of
      * {@link #CAP_MODE_CHARACTERS}, {@link #CAP_MODE_WORDS}, and
      * {@link #CAP_MODE_SENTENCES}.
-     * 
+     *
      * @return Returns the actual capitalization modes that can be in effect
      * at the current position, which is any combination of
      * {@link #CAP_MODE_CHARACTERS}, {@link #CAP_MODE_WORDS}, and
      * {@link #CAP_MODE_SENTENCES}.
      */
     public static int getCapsMode(CharSequence cs, int off, int reqModes) {
+        if (off < 0) {
+            return 0;
+        }
+
         int i;
         char c;
         int mode = 0;
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index d7f2539..0722699 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -320,8 +320,14 @@
      * <p>Calling this overrides any previous call to {@link #setZOrderMediaOverlay}.
      */
     public void setZOrderOnTop(boolean onTop) {
-        mWindowType = onTop ? WindowManager.LayoutParams.TYPE_APPLICATION_PANEL
-                : WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
+        if (onTop) {
+            mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
+            // ensures the surface is placed below the IME
+            mLayout.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+        } else {
+            mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
+            mLayout.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+        }
     }
     
     /**
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 3f1672a..1c0d55f 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -407,7 +407,8 @@
                         }
                     }
                 }
-                CacheManager.trimCacheIfNeeded();
+                WebViewWorker.getHandler().sendEmptyMessage(
+                        WebViewWorker.MSG_TRIM_CACHE);
                 break;
             }
 
diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java
index 647556b..1c59c10 100644
--- a/core/java/android/webkit/CacheManager.java
+++ b/core/java/android/webkit/CacheManager.java
@@ -25,10 +25,11 @@
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.FilenameFilter;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 
 
@@ -200,9 +201,9 @@
             // the cache database. The directory could be recreated
             // because the system flushed all the data/cache directories
             // to free up disk space.
-            WebViewCore.endCacheTransaction();
-            mDataBase.clearCache();
-            WebViewCore.startCacheTransaction();
+            // delete rows in the cache database
+            WebViewWorker.getHandler().sendEmptyMessage(
+                    WebViewWorker.MSG_CLEAR_CACHE);
             return true;
         }
         return false;
@@ -223,7 +224,6 @@
      * 
      * @param disabled true to disable the cache
      */
-    // only called from WebCore thread
     static void setCacheDisabled(boolean disabled) {
         if (disabled == mDisabled) {
             return;
@@ -243,7 +243,7 @@
         return mDisabled;
     }
 
-    // only called from WebCore thread
+    // only called from WebViewWorkerThread
     // make sure to call enableTransaction/disableTransaction in pair
     static boolean enableTransaction() {
         if (++mRefCount == 1) {
@@ -253,12 +253,9 @@
         return false;
     }
 
-    // only called from WebCore thread
+    // only called from WebViewWorkerThread
     // make sure to call enableTransaction/disableTransaction in pair
     static boolean disableTransaction() {
-        if (mRefCount == 0) {
-            Log.e(LOGTAG, "disableTransaction is out of sync");
-        }
         if (--mRefCount == 0) {
             mDataBase.endCacheTransaction();
             return true;
@@ -266,15 +263,15 @@
         return false;
     }
 
-    // only called from WebCore thread
-    // make sure to call startCacheTransaction/endCacheTransaction in pair
-    public static boolean startCacheTransaction() {
+    // only called from WebViewWorkerThread
+    // make sure to call startTransaction/endTransaction in pair
+    static boolean startTransaction() {
         return mDataBase.startCacheTransaction();
     }
 
-    // only called from WebCore thread
-    // make sure to call startCacheTransaction/endCacheTransaction in pair
-    public static boolean endCacheTransaction() {
+    // only called from WebViewWorkerThread
+    // make sure to call startTransaction/endTransaction in pair
+    static boolean endTransaction() {
         boolean ret = mDataBase.endCacheTransaction();
         if (++mTrimCacheCount >= TRIM_CACHE_INTERVAL) {
             mTrimCacheCount = 0;
@@ -283,6 +280,26 @@
         return ret;
     }
 
+    // only called from WebCore Thread
+    // make sure to call startCacheTransaction/endCacheTransaction in pair
+    /**
+     * @deprecated
+     */
+    @Deprecated
+    public static boolean startCacheTransaction() {
+        return false;
+    }
+
+    // only called from WebCore Thread
+    // make sure to call startCacheTransaction/endCacheTransaction in pair
+    /**
+     * @deprecated
+     */
+    @Deprecated
+    public static boolean endCacheTransaction() {
+        return false;
+    }
+
     /**
      * Given a url, returns the CacheResult if exists. Otherwise returns null.
      * If headers are provided and a cache needs validation,
@@ -291,13 +308,11 @@
      * 
      * @return the CacheResult for a given url
      */
-    // only called from WebCore thread
     public static CacheResult getCacheFile(String url,
             Map<String, String> headers) {
         return getCacheFile(url, 0, headers);
     }
 
-    // only called from WebCore thread
     static CacheResult getCacheFile(String url, long postIdentifier,
             Map<String, String> headers) {
         if (mDisabled) {
@@ -368,14 +383,12 @@
      * @hide - hide createCacheFile since it has a parameter of type headers, which is
      * in a hidden package.
      */
-    // only called from WebCore thread
     public static CacheResult createCacheFile(String url, int statusCode,
             Headers headers, String mimeType, boolean forceCache) {
         return createCacheFile(url, statusCode, headers, mimeType, 0,
                 forceCache);
     }
 
-    // only called from WebCore thread
     static CacheResult createCacheFile(String url, int statusCode,
             Headers headers, String mimeType, long postIdentifier,
             boolean forceCache) {
@@ -435,12 +448,10 @@
      * Save the info of a cache file for a given url to the CacheMap so that it
      * can be reused later
      */
-    // only called from WebCore thread
     public static void saveCacheFile(String url, CacheResult cacheRet) {
         saveCacheFile(url, 0, cacheRet);
     }
 
-    // only called from WebCore thread
     static void saveCacheFile(String url, long postIdentifier,
             CacheResult cacheRet) {
         try {
@@ -489,7 +500,6 @@
      * 
      * @return true if it succeeds
      */
-    // only called from WebCore thread
     static boolean removeAllCacheFiles() {
         // Note, this is called before init() when the database is
         // created or upgraded.
@@ -499,7 +509,10 @@
             mClearCacheOnInit = true;
             return true;
         }
-        // delete cache in a separate thread to not block UI.
+        // delete rows in the cache database
+        WebViewWorker.getHandler().sendEmptyMessage(
+                WebViewWorker.MSG_CLEAR_CACHE);
+        // delete cache files in a separate thread to not block UI.
         final Runnable clearCache = new Runnable() {
             public void run() {
                 // delete all cache files
@@ -517,8 +530,6 @@
                 } catch (SecurityException e) {
                     // Ignore SecurityExceptions.
                 }
-                // delete database
-                mDataBase.clearCache();
             }
         };
         new Thread(clearCache).start();
@@ -528,15 +539,13 @@
     /**
      * Return true if the cache is empty.
      */
-    // only called from WebCore thread
     static boolean cacheEmpty() {
         return mDataBase.hasCache();
     }
 
-    // only called from WebCore thread
     static void trimCacheIfNeeded() {
         if (mDataBase.getCacheTotalSize() > CACHE_THRESHOLD) {
-            ArrayList<String> pathList = mDataBase.trimCache(CACHE_TRIM_AMOUNT);
+            List<String> pathList = mDataBase.trimCache(CACHE_TRIM_AMOUNT);
             int size = pathList.size();
             for (int i = 0; i < size; i++) {
                 File f = new File(mBaseDir, pathList.get(i));
@@ -544,9 +553,34 @@
                     Log.e(LOGTAG, f.getPath() + " delete failed.");
                 }
             }
+            // remove the unreferenced files in the cache directory
+            final List<String> fileList = mDataBase.getAllCacheFileNames();
+            if (fileList == null) return;
+            String[] toDelete = mBaseDir.list(new FilenameFilter() {
+                public boolean accept(File dir, String filename) {
+                    if (fileList.contains(filename)) {
+                        return false;
+                    } else {
+                        return true;
+                    }
+                }
+            });
+            if (toDelete == null) return;
+            size = toDelete.length;
+            for (int i = 0; i < size; i++) {
+                File f = new File(mBaseDir, toDelete[i]);
+                if (!f.delete()) {
+                    Log.e(LOGTAG, f.getPath() + " delete failed.");
+                }
+            }
         }
     }
 
+    static void clearCache() {
+        // delete database
+        mDataBase.clearCache();
+    }
+
     private static boolean checkCacheRedirect(int statusCode) {
         if (statusCode == 301 || statusCode == 302 || statusCode == 307) {
             // as 303 can't be cached, we do not return true
diff --git a/core/java/android/webkit/FrameLoader.java b/core/java/android/webkit/FrameLoader.java
index b13c405..7903632 100644
--- a/core/java/android/webkit/FrameLoader.java
+++ b/core/java/android/webkit/FrameLoader.java
@@ -110,7 +110,9 @@
                 return false;
             }
             mNetwork = Network.getInstance(mListener.getContext());
-            return handleHTTPLoad();
+            WebViewWorker.getHandler().obtainMessage(
+                    WebViewWorker.MSG_ADD_HTTPLOADER, this).sendToTarget();
+            return true;
         } else if (handleLocalFile(url, mListener, mSettings)) {
             return true;
         }
@@ -142,24 +144,33 @@
         }
         if (URLUtil.isAssetUrl(url)) {
             // load asset in a separate thread as it involves IO
-            new FileLoader(url, loadListener, FileLoader.TYPE_ASSET, true)
-                    .enqueue();
+            WebViewWorker.getHandler().obtainMessage(
+                    WebViewWorker.MSG_ADD_STREAMLOADER,
+                    new FileLoader(url, loadListener, FileLoader.TYPE_ASSET,
+                            true)).sendToTarget();
             return true;
         } else if (URLUtil.isResourceUrl(url)) {
             // load resource in a separate thread as it involves IO
-            new FileLoader(url, loadListener, FileLoader.TYPE_RES, true)
-                    .enqueue();
+            WebViewWorker.getHandler().obtainMessage(
+                    WebViewWorker.MSG_ADD_STREAMLOADER,
+                    new FileLoader(url, loadListener, FileLoader.TYPE_RES,
+                            true)).sendToTarget();
             return true;
         } else if (URLUtil.isFileUrl(url)) {
             // load file in a separate thread as it involves IO
-            new FileLoader(url, loadListener, FileLoader.TYPE_FILE, settings
-                    .getAllowFileAccess()).enqueue();
+            WebViewWorker.getHandler().obtainMessage(
+                    WebViewWorker.MSG_ADD_STREAMLOADER,
+                    new FileLoader(url, loadListener, FileLoader.TYPE_FILE,
+                            settings.getAllowFileAccess())).sendToTarget();
             return true;
         } else if (URLUtil.isContentUrl(url)) {
             // Send the raw url to the ContentLoader because it will do a
             // permission check and the url has to match.
             // load content in a separate thread as it involves IO
-            new ContentLoader(loadListener.url(), loadListener).enqueue();
+            WebViewWorker.getHandler().obtainMessage(
+                    WebViewWorker.MSG_ADD_STREAMLOADER,
+                    new ContentLoader(loadListener.url(), loadListener))
+                    .sendToTarget();
             return true;
         } else if (URLUtil.isDataUrl(url)) {
             // load data in the current thread to reduce the latency
@@ -172,8 +183,8 @@
         }
         return false;
     }
-    
-    private boolean handleHTTPLoad() {
+
+    boolean handleHTTPLoad() {
         if (mHeaders == null) {
             mHeaders = new HashMap<String, String>();
         }
@@ -229,7 +240,9 @@
         CacheLoader cacheLoader =
                 new CacheLoader(mListener, result);
         mListener.setCacheLoader(cacheLoader);
-        cacheLoader.load();
+        // Load the cached file in a separate thread
+        WebViewWorker.getHandler().obtainMessage(
+                WebViewWorker.MSG_ADD_STREAMLOADER, cacheLoader).sendToTarget();
     }
 
     /*
diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java
index cdc6608..8bacee4 100644
--- a/core/java/android/webkit/LoadListener.java
+++ b/core/java/android/webkit/LoadListener.java
@@ -101,7 +101,6 @@
     private boolean  mCancelled;  // The request has been cancelled.
     private boolean  mAuthFailed;  // indicates that the prev. auth failed
     private CacheLoader mCacheLoader;
-    private CacheManager.CacheResult mCacheResult;
     private boolean  mFromCache = false;
     private HttpAuthHeader mAuthHeader;
     private int      mErrorID = OK;
@@ -301,6 +300,12 @@
      */
     public void headers(Headers headers) {
         if (DebugFlags.LOAD_LISTENER) Log.v(LOGTAG, "LoadListener.headers");
+        // call db (setCookie) in the non-WebCore thread
+        if (mCancelled) return;
+        ArrayList<String> cookies = headers.getSetCookie();
+        for (int i = 0; i < cookies.size(); ++i) {
+            CookieManager.getInstance().setCookie(mUri, cookies.get(i));
+        }
         sendMessageInternal(obtainMessage(MSG_CONTENT_HEADERS, headers));
     }
 
@@ -316,11 +321,6 @@
         if (mCancelled) return;
         mHeaders = headers;
 
-        ArrayList<String> cookies = headers.getSetCookie();
-        for (int i = 0; i < cookies.size(); ++i) {
-            CookieManager.getInstance().setCookie(mUri, cookies.get(i));
-        }
-
         long contentLength = headers.getContentLength();
         if (contentLength != Headers.NO_CONTENT_LENGTH) {
             mContentLength = contentLength;
@@ -454,12 +454,19 @@
             if (!mFromCache && mRequestHandle != null
                     && (!mRequestHandle.getMethod().equals("POST")
                             || mPostIdentifier != 0)) {
-                mCacheResult = CacheManager.createCacheFile(mUrl, mStatusCode,
-                        headers, mMimeType, mPostIdentifier, false);
+                WebViewWorker.CacheCreateData data = new WebViewWorker.CacheCreateData();
+                data.mListener = this;
+                data.mUrl = mUrl;
+                data.mMimeType = mMimeType;
+                data.mStatusCode = mStatusCode;
+                data.mPostId = mPostIdentifier;
+                data.mHeaders = headers;
+                WebViewWorker.getHandler().obtainMessage(
+                        WebViewWorker.MSG_CREATE_CACHE, data).sendToTarget();
             }
-            if (mCacheResult != null) {
-                mCacheResult.encoding = mEncoding;
-            }
+            WebViewWorker.CacheEncoding ce = new WebViewWorker.CacheEncoding();
+            WebViewWorker.getHandler().obtainMessage(
+                    WebViewWorker.MSG_UPDATE_CACHE_ENCODING, ce).sendToTarget();
         }
         commitHeadersCheckRedirect();
     }
@@ -649,7 +656,10 @@
                 // ask for it, so make sure we have a valid CacheLoader
                 // before calling it.
                 if (mCacheLoader != null) {
-                    mCacheLoader.load();
+                    // Load the cached file in a separate thread
+                    WebViewWorker.getHandler().obtainMessage(
+                            WebViewWorker.MSG_ADD_STREAMLOADER, mCacheLoader)
+                            .sendToTarget();
                     mFromCache = true;
                     if (DebugFlags.LOAD_LISTENER) {
                         Log.v(LOGTAG, "LoadListener cache load url=" + url());
@@ -708,8 +718,10 @@
                     Log.v(LOGTAG, "FrameLoader: HTTP URL in cache " +
                             "and usable: " + url());
                 }
-                // Load the cached file
-                mCacheLoader.load();
+                // Load the cached file in a separate thread
+                WebViewWorker.getHandler().obtainMessage(
+                        WebViewWorker.MSG_ADD_STREAMLOADER, mCacheLoader)
+                        .sendToTarget();
                 mFromCache = true;
                 return true;
             }
@@ -934,12 +946,9 @@
      * WebCore.
      */
     void downloadFile() {
-        // Setting the Cache Result to null ensures that this
-        // content is not added to the cache
-        if (mCacheResult != null) {
-            CacheManager.cleanupCacheFile(mCacheResult);
-            mCacheResult = null;
-        }
+        // remove the cache
+        WebViewWorker.getHandler().obtainMessage(
+                WebViewWorker.MSG_REMOVE_CACHE, this).sendToTarget();
 
         // Inform the client that they should download a file
         mBrowserFrame.getCallbackProxy().onDownloadStart(url(), 
@@ -1098,24 +1107,15 @@
             if (c == null) break;
 
             if (c.mLength != 0) {
-                if (mCacheResult != null) {
-                    mCacheResult.contentLength += c.mLength;
-                    if (mCacheResult.contentLength > CacheManager.CACHE_MAX_SIZE) {
-                        CacheManager.cleanupCacheFile(mCacheResult);
-                        mCacheResult = null;
-                    } else {
-                        try {
-                            mCacheResult.outStream
-                                    .write(c.mArray, 0, c.mLength);
-                        } catch (IOException e) {
-                            CacheManager.cleanupCacheFile(mCacheResult);
-                            mCacheResult = null;
-                        }
-                    }
-                }
                 nativeAddData(c.mArray, c.mLength);
+                WebViewWorker.CacheData data = new WebViewWorker.CacheData();
+                data.mListener = this;
+                data.mChunk = c;
+                WebViewWorker.getHandler().obtainMessage(
+                        WebViewWorker.MSG_APPEND_CACHE, data).sendToTarget();
+            } else {
+                c.release();
             }
-            c.release();
             checker.responseAlert("res nativeAddData");
         }
     }
@@ -1125,18 +1125,16 @@
      * cancellation or errors during the load.
      */
     void tearDown() {
-        if (mCacheResult != null) {
-            if (getErrorID() == OK) {
-                CacheManager.saveCacheFile(mUrl, mPostIdentifier, mCacheResult);
-            } else {
-                CacheManager.cleanupCacheFile(mCacheResult);
-            }
-
-            // we need to reset mCacheResult to be null
-            // resource loader's tearDown will call into WebCore's
-            // nativeFinish, which in turn calls loader.cancel().
-            // If we don't reset mCacheFile, the file will be deleted.
-            mCacheResult = null;
+        if (getErrorID() == OK) {
+            WebViewWorker.CacheSaveData data = new WebViewWorker.CacheSaveData();
+            data.mListener = this;
+            data.mUrl = mUrl;
+            data.mPostId = mPostIdentifier;
+            WebViewWorker.getHandler().obtainMessage(
+                    WebViewWorker.MSG_SAVE_CACHE, data).sendToTarget();
+        } else {
+            WebViewWorker.getHandler().obtainMessage(
+                    WebViewWorker.MSG_REMOVE_CACHE, this).sendToTarget();
         }
         if (mNativeLoader != 0) {
             PerfChecker checker = new PerfChecker();
@@ -1194,10 +1192,8 @@
             mRequestHandle = null;
         }
 
-        if (mCacheResult != null) {
-            CacheManager.cleanupCacheFile(mCacheResult);
-            mCacheResult = null;
-        }
+        WebViewWorker.getHandler().obtainMessage(
+                WebViewWorker.MSG_REMOVE_CACHE, this).sendToTarget();
         mCancelled = true;
 
         clearNativeLoader();
@@ -1258,14 +1254,16 @@
             }
 
             // Cache the redirect response
-            if (mCacheResult != null) {
-                if (getErrorID() == OK) {
-                    CacheManager.saveCacheFile(mUrl, mPostIdentifier,
-                            mCacheResult);
-                } else {
-                    CacheManager.cleanupCacheFile(mCacheResult);
-                }
-                mCacheResult = null;
+            if (getErrorID() == OK) {
+                WebViewWorker.CacheSaveData data = new WebViewWorker.CacheSaveData();
+                data.mListener = this;
+                data.mUrl = mUrl;
+                data.mPostId = mPostIdentifier;
+                WebViewWorker.getHandler().obtainMessage(
+                        WebViewWorker.MSG_SAVE_CACHE, data).sendToTarget();
+            } else {
+                WebViewWorker.getHandler().obtainMessage(
+                        WebViewWorker.MSG_REMOVE_CACHE, this).sendToTarget();
             }
 
             // This will strip the anchor
diff --git a/core/java/android/webkit/StreamLoader.java b/core/java/android/webkit/StreamLoader.java
index 4c32997..7bcd50d 100644
--- a/core/java/android/webkit/StreamLoader.java
+++ b/core/java/android/webkit/StreamLoader.java
@@ -20,8 +20,6 @@
 import android.net.http.EventHandler;
 import android.net.http.Headers;
 import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
 import android.os.Message;
 
 import java.io.IOException;
@@ -61,11 +59,6 @@
     // Handler which will be initialized in the thread where load() is called.
     private Handler mHandler;
 
-    // Handler which will be used to load StreamLoader in a separate thread
-    private static StreamQueueHandler sStreamQueueHandler;
-
-    private static final Object sStreamQueueLock = new Object();
-
     /**
      * Constructor. Although this class calls the LoadListener, it only calls
      * the EventHandler Interface methods. LoadListener concrete class is used
@@ -97,26 +90,6 @@
     abstract protected void buildHeaders(Headers headers);
 
     /**
-     * Calling this method to load this StreamLoader in a separate
-     * "StreamLoadingThread".
-     */
-    final void enqueue() {
-        synchronized (sStreamQueueLock) {
-            if (sStreamQueueHandler == null) {
-                HandlerThread thread = new HandlerThread(
-                        StreamQueueHandler.THREAD_NAME,
-                        android.os.Process.THREAD_PRIORITY_DEFAULT +
-                        android.os.Process.THREAD_PRIORITY_LESS_FAVORABLE);
-                thread.start();
-                sStreamQueueHandler = new StreamQueueHandler(thread.getLooper());
-            }
-        }
-
-        sStreamQueueHandler.obtainMessage(StreamQueueHandler.MSG_ADD_LOADER,
-                this).sendToTarget();
-    }
-
-    /**
      * Calling this method starts the load of the content for this StreamLoader.
      * This method simply creates a Handler in the current thread and posts a
      * message to send the status and returns immediately.
@@ -228,22 +201,4 @@
         }
         mLoadListener.endData();
     }
-
-    private static class StreamQueueHandler extends Handler {
-        private static final String THREAD_NAME = "StreamLoadingThread";
-
-        private static final int MSG_ADD_LOADER = 101;
-
-        StreamQueueHandler(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            if (msg.what == MSG_ADD_LOADER) {
-                StreamLoader loader = (StreamLoader) msg.obj;
-                loader.load();
-            }
-        }
-    }
 }
diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java
index d1ad61f..db19bca 100644
--- a/core/java/android/webkit/WebTextView.java
+++ b/core/java/android/webkit/WebTextView.java
@@ -304,15 +304,16 @@
     public void onEditorAction(int actionCode) {
         switch (actionCode) {
         case EditorInfo.IME_ACTION_NEXT:
-            // Since the cursor will no longer be in the same place as the
-            // focus, set the focus controller back to inactive
-            mWebView.setFocusControllerInactive();
-            mWebView.nativeMoveCursorToNextTextInput();
-            // Preemptively rebuild the WebTextView, so that the action will
-            // be set properly.
-            mWebView.rebuildWebTextView();
-            setDefaultSelection();
-            mWebView.invalidate();
+            if (mWebView.nativeMoveCursorToNextTextInput()) {
+                // Since the cursor will no longer be in the same place as the
+                // focus, set the focus controller back to inactive
+                mWebView.setFocusControllerInactive();
+                // Preemptively rebuild the WebTextView, so that the action will
+                // be set properly.
+                mWebView.rebuildWebTextView();
+                setDefaultSelection();
+                mWebView.invalidate();
+            }
             break;
         case EditorInfo.IME_ACTION_DONE:
             super.onEditorAction(actionCode);
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index d29d6f3..897bd75 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -6807,7 +6807,7 @@
     private native void     nativeHideCursor();
     private native String   nativeImageURI(int x, int y);
     private native void     nativeInstrumentReport();
-    /* package */ native void nativeMoveCursorToNextTextInput();
+    /* package */ native boolean nativeMoveCursorToNextTextInput();
     // return true if the page has been scrolled
     private native boolean  nativeMotionUp(int x, int y, int slop);
     // returns false if it handled the key
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 4606bc6..71f69fe 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -587,13 +587,6 @@
         private static final int INITIALIZE = 0;
         private static final int REDUCE_PRIORITY = 1;
         private static final int RESUME_PRIORITY = 2;
-        private static final int CACHE_TICKER = 3;
-        private static final int BLOCK_CACHE_TICKER = 4;
-        private static final int RESUME_CACHE_TICKER = 5;
-
-        private static final int CACHE_TICKER_INTERVAL = 60 * 1000; // 1 minute
-
-        private static boolean mCacheTickersBlocked = true;
 
         public void run() {
             Looper.prepare();
@@ -619,28 +612,6 @@
                                 Process.setThreadPriority(
                                         Process.THREAD_PRIORITY_DEFAULT);
                                 break;
-
-                            case CACHE_TICKER:
-                                if (!mCacheTickersBlocked) {
-                                    CacheManager.endCacheTransaction();
-                                    CacheManager.startCacheTransaction();
-                                    sendMessageDelayed(
-                                            obtainMessage(CACHE_TICKER),
-                                            CACHE_TICKER_INTERVAL);
-                                }
-                                break;
-
-                            case BLOCK_CACHE_TICKER:
-                                if (CacheManager.endCacheTransaction()) {
-                                    mCacheTickersBlocked = true;
-                                }
-                                break;
-
-                            case RESUME_CACHE_TICKER:
-                                if (CacheManager.startCacheTransaction()) {
-                                    mCacheTickersBlocked = false;
-                                }
-                                break;
                         }
                     }
                 };
@@ -1092,23 +1063,15 @@
                             Process.setThreadPriority(mTid,
                                     Process.THREAD_PRIORITY_BACKGROUND);
                             pauseTimers();
-                            if (CacheManager.disableTransaction()) {
-                                WebCoreThread.mCacheTickersBlocked = true;
-                                sWebCoreHandler.removeMessages(
-                                        WebCoreThread.CACHE_TICKER);
-                            }
+                            WebViewWorker.getHandler().sendEmptyMessage(
+                                    WebViewWorker.MSG_PAUSE_CACHE_TRANSACTION);
                             break;
 
                         case RESUME_TIMERS:
                             Process.setThreadPriority(mTid, mSavedPriority);
                             resumeTimers();
-                            if (CacheManager.enableTransaction()) {
-                                WebCoreThread.mCacheTickersBlocked = false;
-                                sWebCoreHandler.sendMessageDelayed(
-                                        sWebCoreHandler.obtainMessage(
-                                        WebCoreThread.CACHE_TICKER),
-                                        WebCoreThread.CACHE_TICKER_INTERVAL);
-                            }
+                            WebViewWorker.getHandler().sendEmptyMessage(
+                                    WebViewWorker.MSG_RESUME_CACHE_TRANSACTION);
                             break;
 
                         case ON_PAUSE:
@@ -1851,16 +1814,6 @@
                 .obtainMessage(WebCoreThread.RESUME_PRIORITY));
     }
 
-    static void startCacheTransaction() {
-        sWebCoreHandler.sendMessage(sWebCoreHandler
-                .obtainMessage(WebCoreThread.RESUME_CACHE_TICKER));
-    }
-
-    static void endCacheTransaction() {
-        sWebCoreHandler.sendMessage(sWebCoreHandler
-                .obtainMessage(WebCoreThread.BLOCK_CACHE_TICKER));
-    }
-
     static void pauseUpdatePicture(WebViewCore core) {
         // Note: there is one possible failure mode. If pauseUpdatePicture() is
         // called from UI thread while WEBKIT_DRAW is just pulled out of the
@@ -1992,9 +1945,10 @@
         sendUpdateTextEntry();
         // as CacheManager can behave based on database transaction, we need to
         // call tick() to trigger endTransaction
-        sWebCoreHandler.removeMessages(WebCoreThread.CACHE_TICKER);
-        sWebCoreHandler.sendMessage(sWebCoreHandler
-                .obtainMessage(WebCoreThread.CACHE_TICKER));
+        WebViewWorker.getHandler().removeMessages(
+                WebViewWorker.MSG_CACHE_TRANSACTION_TICKER);
+        WebViewWorker.getHandler().sendEmptyMessage(
+                WebViewWorker.MSG_CACHE_TRANSACTION_TICKER);
         contentDraw();
     }
 
diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java
index 110e4f8..a870931 100644
--- a/core/java/android/webkit/WebViewDatabase.java
+++ b/core/java/android/webkit/WebViewDatabase.java
@@ -19,6 +19,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Set;
 import java.util.Map.Entry;
 
@@ -234,6 +235,13 @@
             }
 
             if (mCacheDatabase != null) {
+                // use read_uncommitted to speed up READ
+                mCacheDatabase.execSQL("PRAGMA read_uncommitted = true;");
+                // as only READ can be called in the non-WebViewWorkerThread,
+                // and read_uncommitted is used, we can turn off database lock
+                // to use transaction.
+                mCacheDatabase.setLockingEnabled(false);
+
                 // use InsertHelper for faster insertion
                 mCacheInserter = new DatabaseUtils.InsertHelper(mCacheDatabase,
                         "cache");
@@ -548,19 +556,33 @@
     }
 
     //
-    // cache functions, can only be called from WebCoreThread
+    // cache functions
     //
 
+    // only called from WebViewWorkerThread
     boolean startCacheTransaction() {
         if (++mCacheTransactionRefcount == 1) {
+            if (!Thread.currentThread().equals(
+                    WebViewWorker.getHandler().getLooper().getThread())) {
+                Log.w(LOGTAG, "startCacheTransaction should be called from "
+                        + "WebViewWorkerThread instead of from "
+                        + Thread.currentThread().getName());
+            }
             mCacheDatabase.beginTransaction();
             return true;
         }
         return false;
     }
 
+    // only called from WebViewWorkerThread
     boolean endCacheTransaction() {
         if (--mCacheTransactionRefcount == 0) {
+            if (!Thread.currentThread().equals(
+                    WebViewWorker.getHandler().getLooper().getThread())) {
+                Log.w(LOGTAG, "endCacheTransaction should be called from "
+                        + "WebViewWorkerThread instead of from "
+                        + Thread.currentThread().getName());
+            }
             try {
                 mCacheDatabase.setTransactionSuccessful();
             } finally {
@@ -684,7 +706,7 @@
         return size;
     }
 
-    ArrayList<String> trimCache(long amount) {
+    List<String> trimCache(long amount) {
         ArrayList<String> pathList = new ArrayList<String>(100);
         Cursor cursor = mCacheDatabase.rawQuery(
                 "SELECT contentlength, filepath FROM cache ORDER BY expires ASC",
@@ -727,6 +749,20 @@
         return pathList;
     }
 
+    List<String> getAllCacheFileNames() {
+        ArrayList<String> pathList = null;
+        Cursor cursor = mCacheDatabase.rawQuery("SELECT filepath FROM cache",
+                null);
+        if (cursor != null && cursor.moveToFirst()) {
+            pathList = new ArrayList<String>(cursor.getCount());
+            do {
+                pathList.add(cursor.getString(0));
+            } while (cursor.moveToNext());
+        }
+        cursor.close();
+        return pathList;
+    }
+
     //
     // password functions
     //
diff --git a/core/java/android/webkit/WebViewWorker.java b/core/java/android/webkit/WebViewWorker.java
new file mode 100644
index 0000000..c488150
--- /dev/null
+++ b/core/java/android/webkit/WebViewWorker.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import android.net.http.Headers;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+
+/**
+ * WebViewWorker executes in a separate thread other than UI and WebViewCore. To
+ * avoid blocking UI or WebKit's execution, the caller can send a message to
+ * WebViewWorker.getHandler() and it will be handled in the WebViewWorkerThread.
+ */
+final class WebViewWorker extends Handler {
+
+    private static final String THREAD_NAME = "WebViewWorkerThread";
+
+    private static WebViewWorker sWorkerHandler;
+
+    private static Map<LoadListener, CacheManager.CacheResult> mCacheResultMap
+            = new HashMap<LoadListener, CacheManager.CacheResult>();
+
+    /**
+     * Package level class to be used while creating a cache entry.
+     */
+    static class CacheCreateData {
+        LoadListener mListener;
+        String mUrl;
+        String mMimeType;
+        int mStatusCode;
+        long mPostId;
+        Headers mHeaders;
+    }
+
+    /**
+     * Package level class to be used while saving a cache entry.
+     */
+    static class CacheSaveData {
+        LoadListener mListener;
+        String mUrl;
+        long mPostId;
+    }
+
+    /**
+     * Package level class to be used while updating a cache entry's encoding.
+     */
+    static class CacheEncoding {
+        LoadListener mListener;
+        String mEncoding;
+    }
+
+    /**
+     * Package level class to be used while appending data to a cache entry.
+     */
+    static class CacheData {
+        LoadListener mListener;
+        ByteArrayBuilder.Chunk mChunk;
+    }
+
+    static synchronized WebViewWorker getHandler() {
+        if (sWorkerHandler == null) {
+            HandlerThread thread = new HandlerThread(THREAD_NAME,
+                    android.os.Process.THREAD_PRIORITY_DEFAULT
+                            + android.os.Process.THREAD_PRIORITY_LESS_FAVORABLE);
+            thread.start();
+            sWorkerHandler = new WebViewWorker(thread.getLooper());
+        }
+        return sWorkerHandler;
+    }
+
+    private WebViewWorker(Looper looper) {
+        super(looper);
+    }
+
+    // trigger transaction once a minute
+    private static final int CACHE_TRANSACTION_TICKER_INTERVAL = 60 * 1000;
+
+    private static boolean mCacheTickersBlocked = true;
+
+    // message ids
+    static final int MSG_ADD_STREAMLOADER = 101;
+    static final int MSG_ADD_HTTPLOADER = 102;
+    static final int MSG_CREATE_CACHE = 103;
+    static final int MSG_UPDATE_CACHE_ENCODING = 104;
+    static final int MSG_APPEND_CACHE = 105;
+    static final int MSG_SAVE_CACHE = 106;
+    static final int MSG_REMOVE_CACHE = 107;
+    static final int MSG_TRIM_CACHE = 108;
+    static final int MSG_CLEAR_CACHE = 109;
+    static final int MSG_CACHE_TRANSACTION_TICKER = 110;
+    static final int MSG_PAUSE_CACHE_TRANSACTION = 111;
+    static final int MSG_RESUME_CACHE_TRANSACTION = 112;
+
+    @Override
+    public void handleMessage(Message msg) {
+        switch(msg.what) {
+            case MSG_ADD_STREAMLOADER: {
+                StreamLoader loader = (StreamLoader) msg.obj;
+                loader.load();
+                break;
+            }
+            case MSG_ADD_HTTPLOADER: {
+                FrameLoader loader = (FrameLoader) msg.obj;
+                loader.handleHTTPLoad();
+                break;
+            }
+            case MSG_CREATE_CACHE: {
+                CacheCreateData data = (CacheCreateData) msg.obj;
+                CacheManager.CacheResult cache = CacheManager.createCacheFile(
+                        data.mUrl, data.mStatusCode, data.mHeaders,
+                        data.mMimeType, data.mPostId, false);
+                if (cache != null) {
+                    mCacheResultMap.put(data.mListener, cache);
+                } else {
+                    mCacheResultMap.remove(data.mListener);
+                }
+                break;
+            }
+            case MSG_UPDATE_CACHE_ENCODING: {
+                CacheEncoding data = (CacheEncoding) msg.obj;
+                CacheManager.CacheResult cache = mCacheResultMap
+                        .get(data.mListener);
+                if (cache != null) {
+                    cache.encoding = data.mEncoding;
+                }
+                break;
+            }
+            case MSG_APPEND_CACHE: {
+                CacheData data = (CacheData) msg.obj;
+                CacheManager.CacheResult cache = mCacheResultMap
+                        .get(data.mListener);
+                if (cache != null) {
+                    cache.contentLength += data.mChunk.mLength;
+                    if (cache.contentLength > CacheManager.CACHE_MAX_SIZE) {
+                        CacheManager.cleanupCacheFile(cache);
+                        mCacheResultMap.remove(data.mListener);
+                    } else {
+                        try {
+                            cache.outStream.write(data.mChunk.mArray, 0,
+                                    data.mChunk.mLength);
+                        } catch (IOException e) {
+                            CacheManager.cleanupCacheFile(cache);
+                            mCacheResultMap.remove(data.mListener);
+                        }
+                    }
+                }
+                data.mChunk.release();
+                break;
+            }
+            case MSG_SAVE_CACHE: {
+                CacheSaveData data = (CacheSaveData) msg.obj;
+                CacheManager.CacheResult cache = mCacheResultMap
+                        .get(data.mListener);
+                if (cache != null) {
+                    CacheManager.saveCacheFile(data.mUrl, data.mPostId, cache);
+                    mCacheResultMap.remove(data.mListener);
+                }
+                break;
+            }
+            case MSG_REMOVE_CACHE: {
+                LoadListener listener = (LoadListener) msg.obj;
+                CacheManager.CacheResult cache = mCacheResultMap.get(listener);
+                if (cache != null) {
+                    CacheManager.cleanupCacheFile(cache);
+                    mCacheResultMap.remove(listener);
+                }
+                break;
+            }
+            case MSG_TRIM_CACHE: {
+                CacheManager.trimCacheIfNeeded();
+                break;
+            }
+            case MSG_CLEAR_CACHE: {
+                CacheManager.clearCache();
+                break;
+            }
+            case MSG_CACHE_TRANSACTION_TICKER: {
+                if (!mCacheTickersBlocked) {
+                    CacheManager.endTransaction();
+                    CacheManager.startTransaction();
+                    sendEmptyMessageDelayed(MSG_CACHE_TRANSACTION_TICKER,
+                            CACHE_TRANSACTION_TICKER_INTERVAL);
+                }
+                break;
+            }
+            case MSG_PAUSE_CACHE_TRANSACTION: {
+                if (CacheManager.disableTransaction()) {
+                    mCacheTickersBlocked = true;
+                    removeMessages(MSG_CACHE_TRANSACTION_TICKER);
+                }
+                break;
+            }
+            case MSG_RESUME_CACHE_TRANSACTION: {
+                if (CacheManager.enableTransaction()) {
+                    mCacheTickersBlocked = false;
+                    sendEmptyMessageDelayed(MSG_CACHE_TRANSACTION_TICKER,
+                            CACHE_TRANSACTION_TICKER_INTERVAL);
+                }
+                break;
+            }
+        }
+    }
+}
diff --git a/core/java/android/widget/ExpandableListView.java b/core/java/android/widget/ExpandableListView.java
index 79abd4b..9cc8bd5 100644
--- a/core/java/android/widget/ExpandableListView.java
+++ b/core/java/android/widget/ExpandableListView.java
@@ -547,9 +547,10 @@
                 
                 final int groupPos = posMetadata.position.groupPos;
                 final int groupFlatPos = posMetadata.position.flatListPos;
-                
-                smoothScrollToPosition(groupFlatPos + mAdapter.getChildrenCount(groupPos),
-                        groupFlatPos);
+
+                final int shiftedGroupPosition = groupFlatPos + getHeaderViewsCount(); 
+                smoothScrollToPosition(shiftedGroupPosition + mAdapter.getChildrenCount(groupPos),
+                        shiftedGroupPosition);
             }
 
             returnValue = true;
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 394ce0a..35ea0cc 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2276,6 +2276,6 @@
     <!-- Strings for car mode notification -->
     <!-- Shown when car mode is enabled -->
     <string name="car_mode_disable_notification_title">Car mode enabled</string>
-    <string name="car_mode_disable_notification_message">Select to disable car mode.</string>
+    <string name="car_mode_disable_notification_message">Select to exit car mode.</string>
 
 </resources>
diff --git a/mms-common/java/com/android/mmscommon/mms/pdu/PduPersister.java b/mms-common/java/com/android/mmscommon/mms/pdu/PduPersister.java
index 46f28c7..d92f0e1 100644
--- a/mms-common/java/com/android/mmscommon/mms/pdu/PduPersister.java
+++ b/mms-common/java/com/android/mmscommon/mms/pdu/PduPersister.java
@@ -1145,6 +1145,10 @@
             }
         }
 
+        // mark "read" and "seen"
+        values.put(Mms.READ, 0);
+        values.put(Mms.SEEN, 0);
+
         Uri res = SqliteWrapper.insert(mContext, mContentResolver, uri, values);
         if (res == null) {
             throw new MmsException("persist() failed: return null.");
diff --git a/mms-common/java/com/android/mmscommon/telephony/TelephonyProvider.java b/mms-common/java/com/android/mmscommon/telephony/TelephonyProvider.java
index 87e4758..cfc9231 100644
--- a/mms-common/java/com/android/mmscommon/telephony/TelephonyProvider.java
+++ b/mms-common/java/com/android/mmscommon/telephony/TelephonyProvider.java
@@ -51,37 +51,6 @@
     private static final boolean DEBUG = true;
     private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV;
 
-//    public static final Pattern EMAIL_ADDRESS
-//        = Pattern.compile(
-//            "[a-zA-Z0-9\\+\\.\\_\\%\\-]{1,256}" +
-//            "\\@" +
-//            "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}" +
-//            "(" +
-//                "\\." +
-//                "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,25}" +
-//            ")+"
-//        );
-//
-//    /**
-//     * This pattern is intended for searching for things that look like they
-//     * might be phone numbers in arbitrary text, not for validating whether
-//     * something is in fact a phone number.  It will miss many things that
-//     * are legitimate phone numbers.
-//     *
-//     * <p> The pattern matches the following:
-//     * <ul>
-//     * <li>Optionally, a + sign followed immediately by one or more digits. Spaces, dots, or dashes
-//     * may follow.
-//     * <li>Optionally, sets of digits in parentheses, separated by spaces, dots, or dashes.
-//     * <li>A string starting and ending with a digit, containing digits, spaces, dots, and/or dashes.
-//     * </ul>
-//     */
-//    public static final Pattern PHONE
-//        = Pattern.compile(                                  // sdd = space, dot, or dash
-//                "(\\+[0-9]+[\\- \\.]*)?"                    // +<digits><sdd>*
-//                + "(\\([0-9]+\\)[\\- \\.]*)?"               // (<digits>)<sdd>*
-//                + "([0-9][0-9\\- \\.][0-9\\- \\.]+[0-9])"); // <digit><digit|sdd>+<digit>
-
     // Constructor
     public TelephonyProvider() {
     }
@@ -136,6 +105,12 @@
         public static final String READ = "read";
 
         /**
+         * Indicates whether this message has been seen by the user. The "seen" flag will be
+         * used to figure out whether we need to throw up a statusbar notification or not.
+         */
+        public static final String SEEN = "seen";
+
+        /**
          * The TP-Status value for the message, or -1 if no status has
          * been received
          */
@@ -706,6 +681,12 @@
         public static final String READ = "read";
 
         /**
+         * Indicates whether this message has been seen by the user. The "seen" flag will be
+         * used to figure out whether we need to throw up a statusbar notification or not.
+         */
+        public static final String SEEN = "seen";
+
+        /**
          * The Message-ID of the message.
          * <P>Type: TEXT</P>
          */
@@ -1157,6 +1138,7 @@
          * <P>Type: INTEGER</P>
          */
         public static final String READ = "read";
+
         /**
          * The snippet of the latest message in the thread.
          * <P>Type: TEXT</P>
@@ -1697,6 +1679,13 @@
              */
             public static final String LAST_TRY = "last_try";
         }
+
+        public static final class WordsTable {
+            public static final String ID = "_id";
+            public static final String SOURCE_ROW_ID = "source_id";
+            public static final String TABLE_ID = "table_to_use";
+            public static final String INDEXED_TEXT = "index_text";
+        }
     }
 
     public static final class Carriers implements BaseColumns {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index a404ec5..1840ecc 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -1860,11 +1860,16 @@
                 + " app=" + app + " knownToBeDead=" + knownToBeDead
                 + " thread=" + (app != null ? app.thread : null)
                 + " pid=" + (app != null ? app.pid : -1));
-        if (app != null &&
-                (!knownToBeDead || app.thread == null) && app.pid > 0) {
-            return app;
+        if (app != null && app.pid > 0) {
+            if (!knownToBeDead || app.thread == null) {
+                return app;
+            } else {
+                // An application record is attached to a previous process,
+                // clean it up now.
+                handleAppDiedLocked(app, true);
+            }
         }
-        
+
         String hostingNameStr = hostingName != null
                 ? hostingName.flattenToShortString() : null;
         
@@ -4588,7 +4593,9 @@
 
         mProcDeaths[0]++;
         
-        if (app.thread != null && app.thread.asBinder() == thread.asBinder()) {
+        // Clean up already done if the process has been re-started.
+        if (app.pid == pid && app.thread != null &&
+                app.thread.asBinder() == thread.asBinder()) {
             Log.i(TAG, "Process " + app.processName + " (pid " + pid
                     + ") has died.");
             EventLog.writeEvent(EventLogTags.AM_PROC_DIED, app.pid, app.processName);
@@ -4636,6 +4643,11 @@
                     scheduleAppGcsLocked();
                 }
             }
+        } else if (app.pid != pid) {
+            // A new process has already been started.
+            Log.i(TAG, "Process " + app.processName + " (pid " + pid
+                    + ") has died and restarted (pid " + app.pid + ").");
+            EventLog.writeEvent(EventLogTags.AM_PROC_DIED, app.pid, app.processName);
         } else if (DEBUG_PROCESSES) {
             Log.d(TAG, "Received spurious death notification for thread "
                     + thread.asBinder());
@@ -5479,6 +5491,8 @@
                 finishReceiverLocked(br.receiver, br.resultCode, br.resultData,
                         br.resultExtras, br.resultAbort, true);
                 scheduleBroadcastsLocked();
+                // We need to reset the state if we fails to start the receiver.
+                br.state = BroadcastRecord.IDLE;
             }
         }
 
diff --git a/test-runner/src/android/test/IsolatedContext.java b/test-runner/src/android/test/IsolatedContext.java
index 485e45c..f093598 100644
--- a/test-runner/src/android/test/IsolatedContext.java
+++ b/test-runner/src/android/test/IsolatedContext.java
@@ -3,7 +3,11 @@
 import com.google.android.collect.Lists;
 
 import android.accounts.AccountManager;
+import android.accounts.AccountManagerCallback;
+import android.accounts.AccountManagerFuture;
+import android.accounts.AuthenticatorException;
 import android.accounts.OnAccountsUpdateListener;
+import android.accounts.OperationCanceledException;
 import android.accounts.Account;
 import android.content.ContextWrapper;
 import android.content.ContentResolver;
@@ -16,8 +20,13 @@
 import android.net.Uri;
 import android.os.Handler;
 
-import java.util.List;
 import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+import java.util.List;
+
 
 /**
      * A mock context which prevents its users from talking to the rest of the device while
@@ -105,7 +114,58 @@
         public Account[] getAccounts() {
             return new Account[]{};
         }
+
+        public AccountManagerFuture<Account[]> getAccountsByTypeAndFeatures(
+                final String type, final String[] features,
+                AccountManagerCallback<Account[]> callback, Handler handler) {
+            return new MockAccountManagerFuture<Account[]>(new Account[0]);
+        }
+
+        public String blockingGetAuthToken(Account account, String authTokenType,
+                boolean notifyAuthFailure)
+                throws OperationCanceledException, IOException, AuthenticatorException {
+            return null;
+        }
+
+
+        /**
+         * A very simple AccountManagerFuture class
+         * that returns what ever was passed in
+         */
+        private class MockAccountManagerFuture<T>
+                implements AccountManagerFuture<T> {
+
+            T mResult;
+
+            public MockAccountManagerFuture(T result) {
+                mResult = result;
+            }
+
+            public boolean cancel(boolean mayInterruptIfRunning) {
+                return false;
+            }
+
+            public boolean isCancelled() {
+                return false;
+            }
+
+            public boolean isDone() {
+                return true;
+            }
+
+            public T getResult()
+                    throws OperationCanceledException, IOException, AuthenticatorException {
+                return mResult;
+            }
+
+            public T getResult(long timeout, TimeUnit unit)
+                    throws OperationCanceledException, IOException, AuthenticatorException {
+                return getResult();
+            }
+        }
+
     }
+
     @Override
     public File getFilesDir() {
         return new File("/dev/null");
diff --git a/tests/DumpRenderTree/assets/run_layout_tests.py b/tests/DumpRenderTree/assets/run_layout_tests.py
index c3627bb..7f3ef2d 100755
--- a/tests/DumpRenderTree/assets/run_layout_tests.py
+++ b/tests/DumpRenderTree/assets/run_layout_tests.py
@@ -26,6 +26,7 @@
     --time-out-ms (default is 8000 millis) for each test
     --adb-options="-e" passes option string to adb
     --results-directory=..., (default is ./layout-test-results) directory name under which results are stored.
+    --js-engine the JavaScript engine currently in use, determines which set of Android-specific expected results we should use, should be 'jsc' or 'v8'
 """
 
 import logging
@@ -186,6 +187,16 @@
   run_layout_test_cmd_postfix = " -e path \"" + path + "\" -e timeout " + timeout_ms
   if options.rebaseline:
     run_layout_test_cmd_postfix += " -e rebaseline true"
+
+  # If the JS engine is not specified on the command line, try reading the
+  # JS_ENGINE environment  variable, which is used by the build system in
+  # external/webkit/Android.mk.
+  js_engine = options.js_engine
+  if not js_engine:
+    js_engine = os.environ['JS_ENGINE']
+  if js_engine:
+    run_layout_test_cmd_postfix += " -e jsengine " + js_engine
+
   run_layout_test_cmd_postfix += " -w com.android.dumprendertree/.LayoutTestsAutoRunner"
 
   # Call LayoutTestsAutoTest::startLayoutTests.
@@ -297,6 +308,9 @@
                            default=None,
                            dest="ref_directory",
                            help="directory where reference results are stored.")
+  option_parser.add_option("", "--js-engine",
+                           default=None,
+                           help="The JavaScript engine currently in use, which determines which set of Android-specific expected results we should use. Should be 'jsc' or 'v8'.");
 
   options, args = option_parser.parse_args();
   main(options, args)
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
index 6ceb0f9..191b5e9 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
@@ -84,7 +84,6 @@
         // 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
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoRunner.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoRunner.java
index 539d551..e058f32 100755
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoRunner.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoRunner.java
@@ -79,14 +79,17 @@
 
         mSaveImagePath = (String) icicle.get("saveimage");
 
+        mJsEngine = (String) icicle.get("jsengine");
+
         super.onCreate(icicle);
     }
     
-    public String mTestPath = null;
-    public String mSaveImagePath = null;
-    public int mTimeoutInMillis = 0;
-    public int mDelay = 0;
-    public boolean mRebaseline = false;
-    public boolean mLogtime = false;
-    public boolean mGetDrawTime = false;
+    public String mTestPath;
+    public String mSaveImagePath;
+    public int mTimeoutInMillis;
+    public int mDelay;
+    public boolean mRebaseline;
+    public boolean mLogtime;
+    public boolean mGetDrawTime;
+    public String mJsEngine;
 }
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
index 634d683..d9ec3fa 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
@@ -147,6 +147,9 @@
     private MyTestRecorder mResultRecorder;
     private Vector<String> mTestList;
     private boolean mRebaselineResults;
+    // The JavaScript engine currently in use. This determines which set of Android-specific
+    // expected test results we use.
+    private String mJsEngine;
     private String mTestPathPrefix;
     private boolean mFinished;
 
@@ -214,14 +217,24 @@
         return shortName.replaceFirst(LAYOUT_TESTS_ROOT, LAYOUT_TESTS_RESULT_DIR) + "-result.txt";
     }
 
+    // Gets the file which contains WebKit's expected results for this test.
     private String getExpectedResultFile(String test) {
+        // The generic result is at <path>/<name>-expected.txt
+        // First try the Android-specific result at
+        // platform/android-<js-engine>/<path>/<name>-expected.txt
         int pos = test.lastIndexOf('.');
-        if(pos == -1)
+        if (pos == -1)
             return null;
-        String shortName = test.substring(0, pos);
-        return shortName + "-expected.txt";
+        String genericExpectedResult = test.substring(0, pos) + "-expected.txt";
+        String androidExpectedResultsDir = "platform/android-" + mJsEngine + "/";
+        String androidExpectedResult =
+            genericExpectedResult.replaceFirst(LAYOUT_TESTS_ROOT, LAYOUT_TESTS_ROOT + androidExpectedResultsDir);
+        File f = new File(androidExpectedResult);
+        return f.exists() ? androidExpectedResult : genericExpectedResult;
     }
 
+    // Gets the file which contains the actual results of running the test on
+    // Android, generated by a previous run which set a new baseline.
     private String getAndroidExpectedResultFile(String expectedResultFile) {
         return expectedResultFile.replaceFirst(LAYOUT_TESTS_ROOT, ANDROID_EXPECTED_RESULT_DIR);
     }
@@ -282,8 +295,8 @@
         });
 
         String resultFile = getResultFile(test);
-        if(resultFile == null) {
-            //simply ignore this test
+        if (resultFile == null) {
+            // Simply ignore this test.
             return;
         }
         if (mRebaselineResults) {
@@ -339,8 +352,10 @@
         this.mTestList = new Vector<String>();
 
         // Read settings
-        this.mTestPathPrefix = (new File(LAYOUT_TESTS_ROOT + runner.mTestPath)).getAbsolutePath();
-        this.mRebaselineResults = runner.mRebaseline;
+        mTestPathPrefix = (new File(LAYOUT_TESTS_ROOT + runner.mTestPath)).getAbsolutePath();
+        mRebaselineResults = runner.mRebaseline;
+        // JSC is the default JavaScript engine.
+        mJsEngine = runner.mJsEngine == null ? "jsc" : runner.mJsEngine;
 
         int timeout = runner.mTimeoutInMillis;
         if (timeout <= 0) {