Merge "Some MediaStore Tests"
diff --git a/tests/assets/webkit/jsform.html b/tests/assets/webkit/jsform.html
index 1781194..ce55498 100644
--- a/tests/assets/webkit/jsform.html
+++ b/tests/assets/webkit/jsform.html
@@ -24,7 +24,7 @@
             }
         }
     </script>
-    <body onload="window.setTimeout(function() { fireSubmit(); }, 0);">
+    <body onload="window.setTimeout(function() { fireSubmit(); }, 500);">
         javascript form test
         <form id="formId" action="test.html#result" method="post">
             <input type="hidden" name="foo" value="bar" />
diff --git a/tests/src/android/webkit/cts/WaitForLoadUrl.java b/tests/src/android/webkit/cts/WaitForLoadUrl.java
deleted file mode 100644
index 7ccd6d2..0000000
--- a/tests/src/android/webkit/cts/WaitForLoadUrl.java
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit.cts;
-
-import android.cts.util.PollingCheck;
-import android.graphics.Bitmap;
-import android.graphics.Picture;
-import android.os.Looper;
-import android.os.SystemClock;
-import android.test.InstrumentationTestCase;
-import android.webkit.WebChromeClient;
-import android.webkit.WebView;
-import android.webkit.WebView.PictureListener;
-import android.webkit.WebViewClient;
-
-import junit.framework.Assert;
-
-/**
- * This class is used to determine when a page has finished loading.
- *
- * initializeWebView sets a WebViewClient, WebChromeClient, and a
- * PictureListener. If a tests provides its own handler, it must call
- * the corresponding WaitForLoadUrl.on* function.
- *
- * This class only really works correctly with a single WebView active because
- * it acts as a singleton.
- */
-class WaitForLoadUrl extends WebViewClient {
-    private static final WaitForLoadUrl sInstance = new WaitForLoadUrl();
-
-    /**
-     * The maximum time, in milliseconds (10 seconds) to wait for a load
-     * to be triggered.
-     */
-    private static final long LOAD_TIMEOUT = 10000;
-
-    /**
-     * Set to true after onPageFinished is called.
-     */
-    private boolean mLoaded;
-
-    /**
-     * Set to true after onNewPicture is called. Reset when onPageFinished
-     * is called.
-     */
-    private boolean mNewPicture;
-
-    /**
-     * The progress, in percentage, of the page load. Valid values are between
-     * 0 and 100.
-     */
-    private int mProgress;
-
-    /**
-     * Private constructor enforces singleton behavior.
-     */
-    private WaitForLoadUrl() {
-    }
-
-    /**
-     * Returns the singleton instance.
-     */
-    public static WaitForLoadUrl getInstance() {
-        return sInstance;
-    }
-
-    /**
-     * Called from WaitForNewPicture, this is used to indicate that
-     * the page has been drawn.
-     */
-    synchronized public void onNewPicture() {
-        mNewPicture = true;
-        this.notifyAll();
-    }
-
-    /**
-     * Called from WaitForLoadedClient, this is used to clear the picture
-     * draw state so that draws before the URL begins loading don't count.
-     */
-    synchronized public void onPageStarted() {
-        mNewPicture = false; // Earlier paints won't count.
-    }
-
-    /**
-     * Called from WaitForLoadedClient, this is used to indicate that
-     * the page is loaded, but not drawn yet.
-     */
-    synchronized public void onPageFinished() {
-        mLoaded = true;
-        this.notifyAll();
-    }
-
-    /**
-     * Called from the WebChrome client, this sets the current progress
-     * for a page.
-     * @param progress The progress made so far between 0 and 100.
-     */
-    synchronized public void onProgressChanged(int progress) {
-        mProgress = progress;
-        this.notifyAll();
-    }
-
-    /**
-     * Sets the WebViewClient, WebChromeClient, and PictureListener for a
-     * WebView to prepare it for the waitForLoadComplete call. If one
-     * of these handlers needs to be changed, the onPageFinished,
-     * onProgressChanged, or onNewPicture must be called from the callback
-     * class.
-     */
-    public void initializeWebView(InstrumentationTestCase test,
-            final WebView view) {
-        Runnable setup = new Runnable() {
-            @Override
-            public void run() {
-                view.setWebViewClient(new WaitForLoadedClient());
-                view.setPictureListener(new WaitForNewPicture());
-                view.setWebChromeClient(new WaitForProgressClient());
-            }
-        };
-        if (isUiThread()) {
-            setup.run();
-        } else {
-            try {
-                test.runTestOnUiThread(setup);
-            } catch (Throwable t) {
-                Assert.fail("Error initializing WebView for waitForLoadUrl");
-            }
-        }
-        clearLoad();
-    }
-
-    /**
-     * Called whenever a load has been completed so that a subsequent call to
-     * waitForLoadComplete doesn't return immediately. This must be called only
-     * after onPageFinished is received for a loadUrl call or else a callback
-     * will change the state before a subsequent load begins and
-     * waitForLoadComplete will not work properly. Normally this call is not
-     * necessary as it is automatically called as part of waitFor.
-     */
-    synchronized public void clearLoad() {
-        mLoaded = false;
-        mNewPicture = false;
-        mProgress = 0;
-    }
-
-    /**
-     * Wait for a page onPageFinished, onNewPicture and
-     * onProgressChange to reach 100. If that does not occur
-     * before LOAD_TIMEOUT expires there will be a test failure.
-     *
-     * This call may be made on the UI thread or a test thread.
-     * @see WaitForLoadUrl#initializeWebView
-     */
-    public void waitForLoadComplete(WebView webView) {
-        waitFor(webView, new WaitCheck() {
-            @Override
-            public boolean isDone() {
-                return mLoaded && mNewPicture && mProgress == 100;
-            }
-        });
-    }
-
-    /**
-     * Waits for the waitCheck condition to be true or the test times out.
-     * The load state is cleared after waiting.
-     * @param webView The WebView for which the test is running.
-     * @param waitCheck Contains the condition to be checked.
-     */
-    private void waitFor(WebView webView, WaitCheck waitCheck) {
-        if (isUiThread()) {
-            waitOnUiThread(webView, waitCheck);
-        } else {
-            waitOnTestThread(waitCheck);
-        }
-        clearLoad();
-    }
-
-    /**
-     * Uses a polling mechanism, while pumping messages to check when the
-     * waitCheck condition is true.
-     * @param webView The WebView for which the test is running.
-     * @param waitCheck Contains the condition to be checked.
-     */
-    private void waitOnUiThread(final WebView webView,
-            final WaitCheck waitCheck) {
-        new PollingCheck(LOAD_TIMEOUT) {
-            @Override
-            protected boolean check() {
-                pumpMessages(webView);
-                synchronized(this) {
-                    return waitCheck.isDone();
-                }
-            }
-        }.run();
-    }
-
-    /**
-     * Uses a wait/notify to check when the waitCheck condition is true.
-     * @param webView The WebView for which the test is running.
-     * @param waitCheck Contains the condition to be checked.
-     */
-    private synchronized void waitOnTestThread(WaitCheck waitCheck) {
-        try {
-            long waitEnd = SystemClock.uptimeMillis() + LOAD_TIMEOUT;
-            long timeRemaining = LOAD_TIMEOUT;
-            while (!waitCheck.isDone() && timeRemaining > 0) {
-                this.wait(timeRemaining);
-                timeRemaining = waitEnd - SystemClock.uptimeMillis();
-            }
-        } catch (InterruptedException e) {
-            // We'll just drop out of the loop and fail
-        }
-        Assert.assertTrue("Load failed to complete before timeout",
-                waitCheck.isDone());
-    }
-
-    /**
-     * Pumps all currently-queued messages in the UI thread and then exits.
-     * This is useful to force processing while running tests in the UI thread.
-     */
-    private static void pumpMessages(WebView webView) {
-        class ExitLoopException extends RuntimeException {
-        }
-
-        // Force loop to exit when processing this. Loop.quit() doesn't
-        // work because this is the main Loop.
-        webView.getHandler().post(new Runnable() {
-            @Override
-            public void run() {
-                throw new ExitLoopException(); // exit loop!
-            }
-        });
-        try {
-            // Pump messages until our message gets through.
-            Looper.loop();
-        } catch (ExitLoopException e) {
-        }
-    }
-
-    /**
-     * Returns true if the current thread is the UI thread based on the
-     * Looper.
-     */
-    private static boolean isUiThread() {
-        return (Looper.myLooper() == Looper.getMainLooper());
-    }
-
-    private interface WaitCheck {
-        public boolean isDone();
-    }
-
-    /**
-     * A WebChromeClient used to capture the onProgressChanged for use
-     * in waitFor functions. If a test must override the WebChromeClient,
-     * it can derive from this class or call WaitForLoadUrl.onProgressChanged
-     * directly.
-     */
-    public static class WaitForProgressClient extends WebChromeClient {
-        @Override
-        public void onProgressChanged(WebView view, int newProgress) {
-            super.onProgressChanged(view, newProgress);
-            WaitForLoadUrl.getInstance().onProgressChanged(newProgress);
-        }
-    }
-
-    /**
-     * A WebViewClient that captures the onPageFinished for use in
-     * waitFor functions. Using initializeWebView sets the WaitForLoadedClient
-     * into the WebView. If a test needs to set a specific WebViewClient and
-     * needs the waitForLoadComplete capability then it should derive from
-     * WaitForLoadedClient or call WaitForLoadUrl.onPageFinished.
-     */
-    public static class WaitForLoadedClient extends WebViewClient {
-        @Override
-        public void onPageFinished(WebView view, String url) {
-            super.onPageFinished(view, url);
-            WaitForLoadUrl.getInstance().onPageFinished();
-        }
-
-        @Override
-        public void onPageStarted(WebView view, String url, Bitmap favicon) {
-            super.onPageStarted(view, url, favicon);
-            WaitForLoadUrl.getInstance().onPageStarted();
-        }
-    }
-
-    /**
-     * A PictureListener that captures the onNewPicture for use in
-     * waitForLoadComplete. Using initializeWebView sets the PictureListener
-     * into the WebView. If a test needs to set a specific PictureListener and
-     * needs the waitForLoadComplete capability then it should call
-     * WaitForLoadUrl.onNewPicture.
-     */
-    private static class WaitForNewPicture implements PictureListener {
-        @Override
-        public void onNewPicture(WebView view, Picture picture) {
-            WaitForLoadUrl.getInstance().onNewPicture();
-        }
-    }
-}
\ No newline at end of file
diff --git a/tests/src/android/webkit/cts/WebViewOnUiThread.java b/tests/src/android/webkit/cts/WebViewOnUiThread.java
index 8b81463..c9dc914 100644
--- a/tests/src/android/webkit/cts/WebViewOnUiThread.java
+++ b/tests/src/android/webkit/cts/WebViewOnUiThread.java
@@ -16,43 +16,131 @@
 
 package android.webkit.cts;
 
+import android.cts.util.PollingCheck;
 import android.graphics.Bitmap;
+import android.graphics.Picture;
+import android.graphics.Rect;
+import android.os.Bundle;
 import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
 import android.test.InstrumentationTestCase;
+import android.util.DisplayMetrics;
+import android.view.View;
+import android.webkit.DownloadListener;
 import android.webkit.WebBackForwardList;
 import android.webkit.WebChromeClient;
 import android.webkit.WebSettings;
 import android.webkit.WebView;
+import android.webkit.WebView.HitTestResult;
+import android.webkit.WebView.PictureListener;
 import android.webkit.WebViewClient;
 
 import junit.framework.Assert;
 
+import java.io.File;
+
 
 /**
  * Many tests need to run WebView code in the UI thread. This class
- * wraps a WebView so that calls are ensured to arrive on the UI Thread.
+ * wraps a WebView so that calls are ensured to arrive on the UI thread.
+ *
+ * All methods may be run on either the UI thread or test thread.
  */
 public class WebViewOnUiThread {
+    /**
+     * The maximum time, in milliseconds (10 seconds) to wait for a load
+     * to be triggered.
+     */
+    private static final long LOAD_TIMEOUT = 10000;
+
+    /**
+     * Set to true after onPageFinished is called.
+     */
+    private boolean mLoaded;
+
+    /**
+     * Set to true after onNewPicture is called. Reset when onPageStarted
+     * is called.
+     */
+    private boolean mNewPicture;
+
+    /**
+     * The progress, in percentage, of the page load. Valid values are between
+     * 0 and 100.
+     */
+    private int mProgress;
+
+    /**
+     * The test that this class is being used in. Used for runTestOnUiThread.
+     */
     private InstrumentationTestCase mTest;
+
+    /**
+     * The WebView that calls will be made on.
+     */
     private WebView mWebView;
 
     /**
      * Initializes the webView with a WebViewClient, WebChromeClient,
-     * and PictureListener as per WaitForLoadUrl.initializeWebView
-     * to prepare for loadUrl.
+     * and PictureListener to prepare for loadUrlAndWaitForCompletion.
      *
-     * This method should be called during setUp so as to reinitialize
-     * between calls.
+     * A new WebViewOnUiThread should be called during setUp so as to
+     * reinitialize between calls.
      *
      * @param test The test in which this is being run.
      * @param webView The webView that the methods should call.
-     * @see WaitForLoadUrl#initializeWebView
      * @see loadUrlAndWaitForCompletion
      */
     public WebViewOnUiThread(InstrumentationTestCase test, WebView webView) {
         mTest = test;
         mWebView = webView;
-        WaitForLoadUrl.getInstance().initializeWebView(mTest, mWebView);
+        final WebViewClient webViewClient = new WaitForLoadedClient(this);
+        final WebChromeClient webChromeClient = new WaitForProgressClient(this);
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mWebView.setWebViewClient(webViewClient);
+                mWebView.setWebChromeClient(webChromeClient);
+                mWebView.setPictureListener(new WaitForNewPicture());
+            }
+        });
+    }
+
+    /**
+     * Called from WaitForNewPicture, this is used to indicate that
+     * the page has been drawn.
+     */
+    synchronized public void onNewPicture() {
+        mNewPicture = true;
+        this.notifyAll();
+    }
+
+    /**
+     * Called from WaitForLoadedClient, this is used to clear the picture
+     * draw state so that draws before the URL begins loading don't count.
+     */
+    synchronized public void onPageStarted() {
+        mNewPicture = false; // Earlier paints won't count.
+    }
+
+    /**
+     * Called from WaitForLoadedClient, this is used to indicate that
+     * the page is loaded, but not drawn yet.
+     */
+    synchronized public void onPageFinished() {
+        mLoaded = true;
+        this.notifyAll();
+    }
+
+    /**
+     * Called from the WebChrome client, this sets the current progress
+     * for a page.
+     * @param progress The progress made so far between 0 and 100.
+     */
+    synchronized public void onProgressChanged(int progress) {
+        mProgress = progress;
+        this.notifyAll();
     }
 
     public void setWebViewClient(final WebViewClient webViewClient) {
@@ -73,6 +161,33 @@
         });
     }
 
+    public void setPictureListener(final PictureListener pictureListener) {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mWebView.setPictureListener(pictureListener);
+            }
+        });
+    }
+
+    public void setDownloadListener(final DownloadListener listener) {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mWebView.setDownloadListener(listener);
+            }
+        });
+    }
+
+    public void setBackgroundColor(final int color) {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mWebView.setBackgroundColor(color);
+            }
+        });
+    }
+
     public void clearCache(final boolean includeDiskFiles) {
         runOnUiThread(new Runnable() {
             @Override
@@ -109,20 +224,92 @@
         });
     }
 
-    public void reload() {
+    public void removeJavascriptInterface(final String interfaceName) {
         runOnUiThread(new Runnable() {
             @Override
             public void run() {
-                mWebView.reload();
+                mWebView.removeJavascriptInterface(interfaceName);
             }
         });
     }
 
-    public void loadUrl(final String url) {
+    public void addJavascriptInterface(final Object object, final String name) {
         runOnUiThread(new Runnable() {
             @Override
             public void run() {
-                mWebView.loadUrl(url);
+                mWebView.addJavascriptInterface(object, name);
+            }
+        });
+    }
+
+    public void flingScroll(final int vx, final int vy) {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mWebView.flingScroll(vx, vy);
+            }
+        });
+    }
+
+    public void requestFocusNodeHref(final Message hrefMsg) {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mWebView.requestFocusNodeHref(hrefMsg);
+            }
+        });
+    }
+
+    public void requestImageRef(final Message msg) {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mWebView.requestImageRef(msg);
+            }
+        });
+    }
+
+    public void setInitialScale(final int scaleInPercent) {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mWebView.setInitialScale(scaleInPercent);
+            }
+        });
+    }
+
+    public void clearSslPreferences() {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mWebView.clearSslPreferences();
+            }
+        });
+    }
+
+    public void resumeTimers() {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mWebView.resumeTimers();
+            }
+        });
+    }
+
+    public void findNext(final boolean forward) {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mWebView.findNext(forward);
+            }
+        });
+    }
+
+    public void clearMatches() {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mWebView.clearMatches();
             }
         });
     }
@@ -134,125 +321,262 @@
      * @param url The URL to load.
      */
     public void loadUrlAndWaitForCompletion(final String url) {
-        runOnUiThread(new Runnable() {
+        callAndWait(new Runnable() {
             @Override
             public void run() {
                 mWebView.loadUrl(url);
             }
         });
-        WaitForLoadUrl.getInstance().waitForLoadComplete(mWebView);
     }
 
-    public String getTitle() {
-        class TitleGetter implements Runnable {
-            private String mTitle;
-
+    public void loadDataAndWaitForCompletion(final String data,
+            final String mimeType, final String encoding) {
+        callAndWait(new Runnable() {
             @Override
             public void run() {
-                mTitle = mWebView.getTitle();
+                mWebView.loadData(data, mimeType, encoding);
             }
-
-            public String getTitle() {
-                return mTitle;
-            }
-        }
-        TitleGetter titleGetter = new TitleGetter();
-        runOnUiThread(titleGetter);
-        return titleGetter.getTitle();
+        });
     }
 
-    public WebSettings getSettings() {
-        class SettingsGetter implements Runnable {
-            private WebSettings mWebSettings;
-
+    public void loadDataWithBaseURLAndWaitForCompletion(final String baseUrl,
+            final String data, final String mimeType, final String encoding,
+            final String historyUrl) {
+        callAndWait(new Runnable() {
             @Override
             public void run() {
-                mWebSettings = mWebView.getSettings();
+                mWebView.loadDataWithBaseURL(baseUrl, data, mimeType, encoding,
+                        historyUrl);
             }
-
-            public WebSettings getSettings() {
-                return mWebSettings;
-            }
-        }
-
-        SettingsGetter settingsGetter = new SettingsGetter();
-        runOnUiThread(settingsGetter);
-        return settingsGetter.getSettings();
-    }
-
-    public WebBackForwardList copyBackForwardList() {
-        class BackForwardCopier implements Runnable {
-            private WebBackForwardList mBackForwardList;
-
-            @Override
-            public void run() {
-                mBackForwardList = mWebView.copyBackForwardList();
-            }
-
-            public WebBackForwardList getBackForwardList() {
-                return mBackForwardList;
-            }
-        }
-        BackForwardCopier backForwardCopier = new BackForwardCopier();
-        runOnUiThread(backForwardCopier);
-        return backForwardCopier.getBackForwardList();
+        });
     }
 
     /**
-     * @see android.webkit.WebView.WebView#
+     * Reloads a page and waits for it to complete reloading. Use reload
+     * if it is a form resubmission and the onFormResubmission responds
+     * by telling WebView not to resubmit it.
      */
-    public Bitmap getFavicon() {
-        class FaviconGetter implements Runnable {
-            private Bitmap mFavicon;
-
+    public void reloadAndWaitForCompletion() {
+        callAndWait(new Runnable() {
             @Override
             public void run() {
-                mFavicon = mWebView.getFavicon();
+                mWebView.reload();
             }
+        });
+    }
 
-            public Bitmap getFavicon() {
-                return mFavicon;
+    /**
+     * Reload the previous URL. Use reloadAndWaitForCompletion unless
+     * it is a form resubmission and the onFormResubmission responds
+     * by telling WebView not to resubmit it.
+     */
+    public void reload() {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mWebView.reload();
             }
+        });
+    }
+
+    /**
+     * Use this only when JavaScript causes a page load to wait for the
+     * page load to complete. Otherwise use loadUrlAndWaitForCompletion or
+     * similar functions.
+     */
+    public void waitForLoadCompletion() {
+        if (isUiThread()) {
+            waitOnUiThread();
+        } else {
+            waitOnTestThread();
         }
-        FaviconGetter favIconGetter = new FaviconGetter();
-        runOnUiThread(favIconGetter);
-        return favIconGetter.getFavicon();
+        clearLoad();
+    }
+
+    public String getTitle() {
+        return getValue(new ValueGetter<String>() {
+            @Override
+            public String capture() {
+                return mWebView.getTitle();
+            }
+        });
+    }
+
+    public WebSettings getSettings() {
+        return getValue(new ValueGetter<WebSettings>() {
+            @Override
+            public WebSettings capture() {
+                return mWebView.getSettings();
+            }
+        });
+    }
+
+    public WebBackForwardList copyBackForwardList() {
+        return getValue(new ValueGetter<WebBackForwardList>() {
+            @Override
+            public WebBackForwardList capture() {
+                return mWebView.copyBackForwardList();
+            }
+        });
+    }
+
+    public Bitmap getFavicon() {
+        return getValue(new ValueGetter<Bitmap>() {
+            @Override
+            public Bitmap capture() {
+                return mWebView.getFavicon();
+            }
+        });
     }
 
     public String getUrl() {
-        class UrlGetter implements Runnable {
-            private String mUrl;
-
+        return getValue(new ValueGetter<String>() {
             @Override
-            public void run() {
-                mUrl = mWebView.getUrl();
+            public String capture() {
+                return mWebView.getUrl();
             }
-
-            public String getUrl() {
-                return mUrl;
-            }
-        }
-        UrlGetter urlGetter = new UrlGetter();
-        runOnUiThread(urlGetter);
-        return urlGetter.getUrl();
+        });
     }
 
     public int getProgress() {
-        class ProgressGetter implements Runnable {
-            private int mProgress;
-
+        return getValue(new ValueGetter<Integer>() {
             @Override
-            public void run() {
-                mProgress = mWebView.getProgress();
+            public Integer capture() {
+                return mWebView.getProgress();
             }
+        });
+    }
 
-            public int getProgress() {
-                return mProgress;
+    public int getHeight() {
+        return getValue(new ValueGetter<Integer>() {
+            @Override
+            public Integer capture() {
+                return mWebView.getHeight();
             }
-        }
-        ProgressGetter progressGetter = new ProgressGetter();
-        runOnUiThread(progressGetter);
-        return progressGetter.getProgress();
+        });
+    }
+
+    public int getContentHeight() {
+        return getValue(new ValueGetter<Integer>() {
+            @Override
+            public Integer capture() {
+                return mWebView.getContentHeight();
+            }
+        });
+    }
+
+    public boolean savePicture(final Bundle b, final File dest) {
+        return getValue(new ValueGetter<Boolean>() {
+            @Override
+            public Boolean capture() {
+                return mWebView.savePicture(b, dest);
+            }
+        });
+    }
+
+    public boolean pageUp(final boolean top) {
+        return getValue(new ValueGetter<Boolean>() {
+            @Override
+            public Boolean capture() {
+                return mWebView.pageUp(top);
+            }
+        });
+    }
+
+    public boolean pageDown(final boolean bottom) {
+        return getValue(new ValueGetter<Boolean>() {
+            @Override
+            public Boolean capture() {
+                return mWebView.pageDown(bottom);
+            }
+        });
+    }
+
+    public int[] getLocationOnScreen() {
+        final int[] location = new int[2];
+        return getValue(new ValueGetter<int[]>() {
+            @Override
+            public int[] capture() {
+                mWebView.getLocationOnScreen(location);
+                return location;
+            }
+        });
+    }
+
+    public float getScale() {
+        return getValue(new ValueGetter<Float>() {
+            @Override
+            public Float capture() {
+                return mWebView.getScale();
+            }
+        });
+    }
+
+    public boolean requestFocus(final int direction,
+            final Rect previouslyFocusedRect) {
+        return getValue(new ValueGetter<Boolean>() {
+            @Override
+            public Boolean capture() {
+                return mWebView.requestFocus(direction, previouslyFocusedRect);
+            }
+        });
+    }
+
+    public HitTestResult getHitTestResult() {
+        return getValue(new ValueGetter<HitTestResult>() {
+            @Override
+            public HitTestResult capture() {
+                return mWebView.getHitTestResult();
+            }
+        });
+    }
+
+    public int getScrollX() {
+        return getValue(new ValueGetter<Integer>() {
+            @Override
+            public Integer capture() {
+                return mWebView.getScrollX();
+            }
+        });
+    }
+
+    public int getScrollY() {
+        return getValue(new ValueGetter<Integer>() {
+            @Override
+            public Integer capture() {
+                return mWebView.getScrollY();
+            }
+        });
+    }
+
+    public final DisplayMetrics getDisplayMetrics() {
+        return getValue(new ValueGetter<DisplayMetrics>() {
+            @Override
+            public DisplayMetrics capture() {
+                return mWebView.getContext().getResources().getDisplayMetrics();
+            }
+        });
+    }
+
+    public boolean requestChildRectangleOnScreen(final View child,
+            final Rect rect,
+            final boolean immediate) {
+        return getValue(new ValueGetter<Boolean>() {
+            @Override
+            public Boolean capture() {
+                return mWebView.requestChildRectangleOnScreen(child, rect,
+                        immediate);
+            }
+        });
+    }
+
+    public int findAll(final String find) {
+        return getValue(new ValueGetter<Integer>() {
+            @Override
+            public Integer capture() {
+                return mWebView.findAll(find);
+            }
+        });
     }
 
     /**
@@ -271,7 +595,8 @@
                 mTest.runTestOnUiThread(r);
             }
         } catch (Throwable t) {
-            Assert.fail("Unexpected error while running on UI thread.");
+            Assert.fail("Unexpected error while running on UI thread: "
+                    + t.getMessage());
         }
     }
 
@@ -283,6 +608,26 @@
         return mWebView;
     }
 
+    private <T> T getValue(ValueGetter<T> getter) {
+        runOnUiThread(getter);
+        return getter.getValue();
+    }
+
+    private abstract class ValueGetter<T> implements Runnable {
+        private T mValue;
+
+        @Override
+        public void run() {
+            mValue = capture();
+        }
+
+        protected abstract T capture();
+
+        public T getValue() {
+           return mValue;
+        }
+    }
+
     /**
      * Returns true if the current thread is the UI thread based on the
      * Looper.
@@ -290,4 +635,150 @@
     private static boolean isUiThread() {
         return (Looper.myLooper() == Looper.getMainLooper());
     }
+
+    /**
+     * @return Whether or not the load has finished.
+     */
+    private synchronized boolean isLoaded() {
+        return mLoaded && mNewPicture && mProgress == 100;
+    }
+
+    /**
+     * Makes a WebView call, waits for completion and then resets the
+     * load state in preparation for the next load call.
+     * @param call The call to make on the UI thread prior to waiting.
+     */
+    private void callAndWait(Runnable call) {
+        Assert.assertTrue("WebViewOnUiThread.load*AndWaitForCompletion calls "
+                + "may not be mixed with load* calls directly on WebView "
+                + "without calling waitForLoadCompletion after the load",
+                !mLoaded);
+        runOnUiThread(call);
+        waitForLoadCompletion();
+    }
+
+    /**
+     * Called whenever a load has been completed so that a subsequent call to
+     * waitForLoadCompletion doesn't return immediately.
+     */
+    synchronized private void clearLoad() {
+        mLoaded = false;
+        mNewPicture = false;
+        mProgress = 0;
+    }
+
+    /**
+     * Uses a polling mechanism, while pumping messages to check when the
+     * load completes.
+     */
+    private void waitOnUiThread() {
+        new PollingCheck(LOAD_TIMEOUT) {
+            @Override
+            protected boolean check() {
+                pumpMessages();
+                return isLoaded();
+            }
+        }.run();
+    }
+
+    /**
+     * Uses a wait/notify to check when the load completes.
+     */
+    private synchronized void waitOnTestThread() {
+        try {
+            long waitEnd = SystemClock.uptimeMillis() + LOAD_TIMEOUT;
+            long timeRemaining = LOAD_TIMEOUT;
+            while (!isLoaded() && timeRemaining > 0) {
+                this.wait(timeRemaining);
+                timeRemaining = waitEnd - SystemClock.uptimeMillis();
+            }
+        } catch (InterruptedException e) {
+            // We'll just drop out of the loop and fail
+        }
+        Assert.assertTrue("Load failed to complete before timeout", isLoaded());
+    }
+
+    /**
+     * Pumps all currently-queued messages in the UI thread and then exits.
+     * This is useful to force processing while running tests in the UI thread.
+     */
+    private void pumpMessages() {
+        class ExitLoopException extends RuntimeException {
+        }
+
+        // Force loop to exit when processing this. Loop.quit() doesn't
+        // work because this is the main Loop.
+        mWebView.getHandler().post(new Runnable() {
+            @Override
+            public void run() {
+                throw new ExitLoopException(); // exit loop!
+            }
+        });
+        try {
+            // Pump messages until our message gets through.
+            Looper.loop();
+        } catch (ExitLoopException e) {
+        }
+    }
+
+    /**
+     * A WebChromeClient used to capture the onProgressChanged for use
+     * in waitFor functions. If a test must override the WebChromeClient,
+     * it can derive from this class or call onProgressChanged
+     * directly.
+     */
+    public static class WaitForProgressClient extends WebChromeClient {
+        private WebViewOnUiThread mOnUiThread;
+
+        public WaitForProgressClient(WebViewOnUiThread onUiThread) {
+            mOnUiThread = onUiThread;
+        }
+
+        @Override
+        public void onProgressChanged(WebView view, int newProgress) {
+            super.onProgressChanged(view, newProgress);
+            mOnUiThread.onProgressChanged(newProgress);
+        }
+    }
+
+    /**
+     * A WebViewClient that captures the onPageFinished for use in
+     * waitFor functions. Using initializeWebView sets the WaitForLoadedClient
+     * into the WebView. If a test needs to set a specific WebViewClient and
+     * needs the waitForCompletion capability then it should derive from
+     * WaitForLoadedClient or call WebViewOnUiThread.onPageFinished.
+     */
+    public static class WaitForLoadedClient extends WebViewClient {
+        private WebViewOnUiThread mOnUiThread;
+
+        public WaitForLoadedClient(WebViewOnUiThread onUiThread) {
+            mOnUiThread = onUiThread;
+        }
+
+        @Override
+        public void onPageFinished(WebView view, String url) {
+            super.onPageFinished(view, url);
+            mOnUiThread.onPageFinished();
+        }
+
+        @Override
+        public void onPageStarted(WebView view, String url, Bitmap favicon) {
+            super.onPageStarted(view, url, favicon);
+            mOnUiThread.onPageStarted();
+        }
+    }
+
+    /**
+     * A PictureListener that captures the onNewPicture for use in
+     * waitForLoadCompletion. Using initializeWebView sets the PictureListener
+     * into the WebView. If a test needs to set a specific PictureListener and
+     * needs the waitForCompletion capability then it should call
+     * WebViewOnUiThread.onNewPicture.
+     */
+    private class WaitForNewPicture implements PictureListener {
+        @Override
+        public void onNewPicture(WebView view, Picture picture) {
+            WebViewOnUiThread.this.onNewPicture();
+        }
+    }
 }
diff --git a/tests/tests/permission2/AndroidManifest.xml b/tests/tests/permission2/AndroidManifest.xml
index e8d3a3a..8f77fad 100755
--- a/tests/tests/permission2/AndroidManifest.xml
+++ b/tests/tests/permission2/AndroidManifest.xml
@@ -39,6 +39,8 @@
     <!--  need app that has CALL_PHONE but not PROCESS_OUTGOING_CALL -->
     <uses-permission android:name="android.permission.CALL_PHONE"/>
 
+    <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
+
     <instrumentation android:name="android.test.InstrumentationTestRunner"
                      android:targetPackage="com.android.cts.permission2"
                      android:label="More CTS tests for permissions"/>
diff --git a/tests/tests/permission2/src/android/permission2/cts/ReadSocialStreamPermissionTest.java b/tests/tests/permission2/src/android/permission2/cts/ReadSocialStreamPermissionTest.java
new file mode 100644
index 0000000..eaf6fdf
--- /dev/null
+++ b/tests/tests/permission2/src/android/permission2/cts/ReadSocialStreamPermissionTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2012 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.permission2.cts;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.net.Uri;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.StreamItems;
+import android.test.AndroidTestCase;
+
+public class ReadSocialStreamPermissionTest extends AndroidTestCase {
+
+    private ContentResolver mResolver;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mResolver = mContext.getContentResolver();
+    }
+
+    public void testReadSocialStreamPermission_byRawContactId() throws Exception {
+        try {
+            mResolver.query(Uri.withAppendedPath(
+                    ContentUris.withAppendedId(RawContacts.CONTENT_URI, 1337),
+                    Contacts.StreamItems.CONTENT_DIRECTORY), null, null, null, null);
+            fail("Expected a READ_SOCIAL_STREAM exception");
+        } catch (SecurityException e) {
+            // Expect a READ_SOCIAL_STREAM exception.
+        }
+    }
+
+    public void testReadSocialStreamPermission_byContactId() throws Exception {
+        try {
+            mResolver.query(Uri.withAppendedPath(
+                    ContentUris.withAppendedId(Contacts.CONTENT_URI, 1337),
+                    Contacts.StreamItems.CONTENT_DIRECTORY), null, null, null, null);
+            fail("Expected a READ_SOCIAL_STREAM exception");
+        } catch (SecurityException e) {
+            // Expect a READ_SOCIAL_STREAM exception.
+        }
+    }
+
+    public void testReadSocialStreamPermission_byLookUpKey() throws Exception {
+        try {
+            mResolver.query(Contacts.CONTENT_LOOKUP_URI.buildUpon()
+                    .appendPath("lookDownKey")
+                    .appendPath(Contacts.StreamItems.CONTENT_DIRECTORY)
+                    .build(), null, null, null, null);
+            fail("Expected a READ_SOCIAL_STREAM exception");
+        } catch (SecurityException e) {
+            // Expect a READ_SOCIAL_STREAM exception.
+        }
+    }
+
+    public void testReadSocialStreamPermission_byStreamItemId() throws Exception {
+        try {
+            mResolver.query(ContentUris.withAppendedId(StreamItems.CONTENT_URI, 1337),
+                    null, null, null, null);
+            fail("Expected a READ_SOCIAL_STREAM exception");
+        } catch (SecurityException e) {
+            // Expect a READ_SOCIAL_STREAM exception.
+        }
+    }
+}
diff --git a/tests/tests/permission2/src/android/permission2/cts/WriteSocialStreamPermissionTest.java b/tests/tests/permission2/src/android/permission2/cts/WriteSocialStreamPermissionTest.java
new file mode 100644
index 0000000..262fcfa
--- /dev/null
+++ b/tests/tests/permission2/src/android/permission2/cts/WriteSocialStreamPermissionTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2012 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.permission2.cts;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.net.Uri;
+import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.StreamItems;
+import android.test.AndroidTestCase;
+
+public class WriteSocialStreamPermissionTest extends AndroidTestCase {
+
+    private ContentResolver mResolver;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mResolver = mContext.getContentResolver();
+    }
+
+    public void testWriteSocialStreamPermission_byContentDirectory() throws Exception {
+        try {
+            ContentValues values = new ContentValues();
+            mResolver.insert(Uri.withAppendedPath(
+                    ContentUris.withAppendedId(RawContacts.CONTENT_URI, 1337),
+                    RawContacts.StreamItems.CONTENT_DIRECTORY), values);
+            fail("Expected a WRITE_SOCIAL_STREAM exception");
+        } catch (SecurityException e) {
+            // Expect a WRITE_SOCIAL_STREAM exception.
+        }
+    }
+
+    public void testWriteSocialStreamPermission_byContentUri() throws Exception {
+        try {
+            ContentValues values = new ContentValues();
+            mResolver.insert(StreamItems.CONTENT_URI, values);
+            fail("Expected a WRITE_SOCIAL_STREAM exception");
+        } catch (SecurityException e) {
+            // Expect a WRITE_SOCIAL_STREAM exception.
+        }
+    }
+}
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_CommonDataKinds_EmailTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_CommonDataKinds_EmailTest.java
new file mode 100644
index 0000000..3f4bd7e
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_CommonDataKinds_EmailTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2012 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.provider.cts;
+
+import android.content.res.Resources;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.test.AndroidTestCase;
+
+public class ContactsContract_CommonDataKinds_EmailTest extends AndroidTestCase {
+
+    private Resources mResources;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mResources = mContext.getResources();
+    }
+
+    public void testGetTypeLabel() {
+        assertGetTypeLabel(Email.TYPE_HOME);
+        assertGetTypeLabel(Email.TYPE_MOBILE);
+        assertGetTypeLabel(Email.TYPE_OTHER);
+        assertGetTypeLabel(Email.TYPE_WORK);
+        assertGetTypeLabel(Email.TYPE_CUSTOM);
+        assertCustomTypeLabel("Custom Label");
+    }
+
+    private void assertGetTypeLabel(int type) {
+        int res = Email.getTypeLabelResource(type);
+        assertTrue(res != 0);
+
+        String label = mResources.getString(res);
+        assertEquals(label, Email.getTypeLabel(mResources, type, ""));
+    }
+
+    private void assertCustomTypeLabel(String label) {
+        int res = Email.getTypeLabelResource(Email.TYPE_CUSTOM);
+        assertTrue(res != 0);
+        assertEquals(label, Email.getTypeLabel(mResources, Email.TYPE_CUSTOM, label));
+    }
+}
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_CommonDataKinds_EventTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_CommonDataKinds_EventTest.java
new file mode 100644
index 0000000..b996ad2
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_CommonDataKinds_EventTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2012 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.provider.cts;
+
+import android.provider.ContactsContract.CommonDataKinds.Event;
+import android.test.AndroidTestCase;
+
+public class ContactsContract_CommonDataKinds_EventTest extends AndroidTestCase {
+
+    public void testGetTypeLabel() {
+        assertGetTypeLabel(Event.TYPE_ANNIVERSARY);
+        assertGetTypeLabel(Event.TYPE_BIRTHDAY);
+        assertGetTypeLabel(Event.TYPE_OTHER);
+        assertGetTypeLabel(Event.TYPE_CUSTOM);
+        assertGetTypeLabel(null);
+    }
+
+    private void assertGetTypeLabel(Integer type) {
+        int res = Event.getTypeResource(type);
+        assertTrue(res != 0);
+    }
+}
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_CommonDataKinds_ImTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_CommonDataKinds_ImTest.java
new file mode 100644
index 0000000..706b933
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_CommonDataKinds_ImTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2012 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.provider.cts;
+
+import android.content.res.Resources;
+import android.provider.ContactsContract.CommonDataKinds.Im;
+import android.test.AndroidTestCase;
+
+public class ContactsContract_CommonDataKinds_ImTest extends AndroidTestCase {
+
+    private Resources mResources;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mResources = mContext.getResources();
+    }
+
+    public void testGetProtocolLabel() {
+        assertGetProtocolLabel(Im.PROTOCOL_AIM);
+        assertGetProtocolLabel(Im.PROTOCOL_CUSTOM);
+        assertGetProtocolLabel(Im.PROTOCOL_GOOGLE_TALK);
+        assertGetProtocolLabel(Im.PROTOCOL_ICQ);
+        assertGetProtocolLabel(Im.PROTOCOL_JABBER);
+        assertGetProtocolLabel(Im.PROTOCOL_MSN);
+        assertGetProtocolLabel(Im.PROTOCOL_NETMEETING);
+        assertGetProtocolLabel(Im.PROTOCOL_QQ);
+        assertGetProtocolLabel(Im.PROTOCOL_SKYPE);
+        assertGetProtocolLabel(Im.PROTOCOL_YAHOO);
+        assertCustomProtocolLabel("Custom Label");
+    }
+
+    public void testGetTypeLabel() {
+        assertGetTypeLabel(Im.TYPE_HOME);
+        assertGetTypeLabel(Im.TYPE_WORK);
+        assertGetTypeLabel(Im.TYPE_OTHER);
+        assertGetTypeLabel(Im.TYPE_CUSTOM);
+        assertCustomTypeLabel("Custom Label");
+    }
+
+    private void assertGetProtocolLabel(int type) {
+        int res = Im.getProtocolLabelResource(type);
+        assertTrue(res != 0);
+
+        String label = mResources.getString(res);
+        assertEquals(label, Im.getProtocolLabel(mResources, type, ""));
+    }
+
+    private void assertCustomProtocolLabel(String label) {
+        int res = Im.getProtocolLabelResource(Im.TYPE_CUSTOM);
+        assertTrue(res != 0);
+        assertEquals(label, Im.getProtocolLabel(mResources, Im.TYPE_CUSTOM, label));
+    }
+
+    private void assertGetTypeLabel(int type) {
+        int res = Im.getTypeLabelResource(type);
+        assertTrue(res != 0);
+
+        String label = mResources.getString(res);
+        assertEquals(label, Im.getTypeLabel(mResources, type, ""));
+    }
+
+    private void assertCustomTypeLabel(String label) {
+        int res = Im.getTypeLabelResource(Im.TYPE_CUSTOM);
+        assertTrue(res != 0);
+        assertEquals(label, Im.getTypeLabel(mResources, Im.TYPE_CUSTOM, label));
+    }
+}
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_CommonDataKinds_OrganizationTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_CommonDataKinds_OrganizationTest.java
new file mode 100644
index 0000000..2de3b05
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_CommonDataKinds_OrganizationTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2012 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.provider.cts;
+
+import android.content.res.Resources;
+import android.provider.ContactsContract.CommonDataKinds.Im;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
+import android.test.AndroidTestCase;
+
+public class ContactsContract_CommonDataKinds_OrganizationTest extends AndroidTestCase {
+
+    private Resources mResources;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mResources = mContext.getResources();
+    }
+
+    public void testGetTypeLabel() {
+        assertGetTypeLabel(Organization.TYPE_WORK);
+        assertGetTypeLabel(Organization.TYPE_OTHER);
+        assertGetTypeLabel(Organization.TYPE_CUSTOM);
+        assertCustomTypeLabel("Custom Label");
+    }
+
+    private void assertGetTypeLabel(int type) {
+        int res = Organization.getTypeLabelResource(type);
+        assertTrue(res != 0);
+
+        String label = mResources.getString(res);
+        assertEquals(label, Organization.getTypeLabel(mResources, type, ""));
+    }
+
+    private void assertCustomTypeLabel(String label) {
+        int res = Organization.getTypeLabelResource(Im.TYPE_CUSTOM);
+        assertTrue(res != 0);
+        assertEquals(label, Organization.getTypeLabel(mResources, Im.TYPE_CUSTOM, label));
+    }
+}
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_CommonDataKinds_PhoneTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_CommonDataKinds_PhoneTest.java
new file mode 100644
index 0000000..433a32e
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_CommonDataKinds_PhoneTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2012 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.provider.cts;
+
+import android.content.res.Resources;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.test.AndroidTestCase;
+
+public class ContactsContract_CommonDataKinds_PhoneTest extends AndroidTestCase {
+
+    private Resources mResources;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mResources = mContext.getResources();
+    }
+
+    public void testGetTypeLabel() {
+        assertGetTypeLabel(Phone.TYPE_ASSISTANT);
+        assertGetTypeLabel(Phone.TYPE_CALLBACK);
+        assertGetTypeLabel(Phone.TYPE_CAR);
+        assertGetTypeLabel(Phone.TYPE_COMPANY_MAIN);
+        assertGetTypeLabel(Phone.TYPE_FAX_HOME);
+        assertGetTypeLabel(Phone.TYPE_FAX_WORK);
+        assertGetTypeLabel(Phone.TYPE_HOME);
+        assertGetTypeLabel(Phone.TYPE_ISDN);
+        assertGetTypeLabel(Phone.TYPE_MAIN);
+        assertGetTypeLabel(Phone.TYPE_MMS);
+        assertGetTypeLabel(Phone.TYPE_MOBILE);
+        assertGetTypeLabel(Phone.TYPE_OTHER);
+        assertGetTypeLabel(Phone.TYPE_OTHER_FAX);
+        assertGetTypeLabel(Phone.TYPE_PAGER);
+        assertGetTypeLabel(Phone.TYPE_RADIO);
+        assertGetTypeLabel(Phone.TYPE_TELEX);
+        assertGetTypeLabel(Phone.TYPE_TTY_TDD);
+        assertGetTypeLabel(Phone.TYPE_WORK);
+        assertGetTypeLabel(Phone.TYPE_WORK_MOBILE);
+        assertGetTypeLabel(Phone.TYPE_WORK_PAGER);
+        assertGetTypeLabel(Phone.TYPE_CUSTOM);
+        assertCustomTypeLabel("Custom Label");
+    }
+
+    private void assertGetTypeLabel(int type) {
+        int res = Phone.getTypeLabelResource(type);
+        assertTrue(res != 0);
+
+        String label = mResources.getString(res);
+        assertEquals(label, Phone.getTypeLabel(mResources, type, ""));
+    }
+
+    private void assertCustomTypeLabel(String label) {
+        int res = Phone.getTypeLabelResource(Phone.TYPE_CUSTOM);
+        assertTrue(res != 0);
+        assertEquals(label, Phone.getTypeLabel(mResources, Phone.TYPE_CUSTOM, label));
+    }
+}
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_CommonDataKinds_RelationTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_CommonDataKinds_RelationTest.java
new file mode 100644
index 0000000..f5e27ce
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_CommonDataKinds_RelationTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2012 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.provider.cts;
+
+import android.content.res.Resources;
+import android.provider.ContactsContract.CommonDataKinds.Relation;
+import android.test.AndroidTestCase;
+
+public class ContactsContract_CommonDataKinds_RelationTest extends AndroidTestCase {
+
+    private Resources mResources;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mResources = mContext.getResources();
+    }
+
+    public void testGetTypeLabel() {
+        assertGetTypeLabel(Relation.TYPE_ASSISTANT);
+        assertGetTypeLabel(Relation.TYPE_BROTHER);
+        assertGetTypeLabel(Relation.TYPE_CHILD);
+        assertGetTypeLabel(Relation.TYPE_DOMESTIC_PARTNER);
+        assertGetTypeLabel(Relation.TYPE_FATHER);
+        assertGetTypeLabel(Relation.TYPE_FRIEND);
+        assertGetTypeLabel(Relation.TYPE_MANAGER);
+        assertGetTypeLabel(Relation.TYPE_MOTHER);
+        assertGetTypeLabel(Relation.TYPE_PARENT);
+        assertGetTypeLabel(Relation.TYPE_PARTNER);
+        assertGetTypeLabel(Relation.TYPE_REFERRED_BY);
+        assertGetTypeLabel(Relation.TYPE_RELATIVE);
+        assertGetTypeLabel(Relation.TYPE_SISTER);
+        assertGetTypeLabel(Relation.TYPE_SPOUSE);
+        assertGetTypeLabel(Relation.TYPE_CUSTOM);
+        assertCustomTypeLabel("Custom Label");
+    }
+
+    private void assertGetTypeLabel(int type) {
+        int res = Relation.getTypeLabelResource(type);
+        assertTrue(res != 0);
+
+        String label = mResources.getString(res);
+        assertEquals(label, Relation.getTypeLabel(mResources, type, ""));
+    }
+
+    private void assertCustomTypeLabel(String label) {
+        int res = Relation.getTypeLabelResource(Relation.TYPE_CUSTOM);
+        assertTrue(res != 0);
+        assertEquals(label, Relation.getTypeLabel(mResources, Relation.TYPE_CUSTOM, label));
+    }
+}
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_CommonDataKinds_SipAddressTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_CommonDataKinds_SipAddressTest.java
new file mode 100644
index 0000000..1406b40
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_CommonDataKinds_SipAddressTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2012 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.provider.cts;
+
+import android.content.res.Resources;
+import android.provider.ContactsContract.CommonDataKinds.SipAddress;
+import android.test.AndroidTestCase;
+
+public class ContactsContract_CommonDataKinds_SipAddressTest extends AndroidTestCase {
+
+    private Resources mResources;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mResources = mContext.getResources();
+    }
+
+    public void testGetTypeLabel() {
+        assertGetTypeLabel(SipAddress.TYPE_HOME);
+        assertGetTypeLabel(SipAddress.TYPE_OTHER);
+        assertGetTypeLabel(SipAddress.TYPE_WORK);
+        assertGetTypeLabel(SipAddress.TYPE_CUSTOM);
+        assertCustomTypeLabel("Custom Label");
+    }
+
+    private void assertGetTypeLabel(int type) {
+        int res = SipAddress.getTypeLabelResource(type);
+        assertTrue(res != 0);
+
+        String label = mResources.getString(res);
+        assertEquals(label, SipAddress.getTypeLabel(mResources, type, ""));
+    }
+
+    private void assertCustomTypeLabel(String label) {
+        int res = SipAddress.getTypeLabelResource(SipAddress.TYPE_CUSTOM);
+        assertTrue(res != 0);
+        assertEquals(label, SipAddress.getTypeLabel(mResources, SipAddress.TYPE_CUSTOM, label));
+    }
+}
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_CommonDataKinds_StructuredPostalTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_CommonDataKinds_StructuredPostalTest.java
new file mode 100644
index 0000000..18a64ce
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_CommonDataKinds_StructuredPostalTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2012 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.provider.cts;
+
+import android.content.res.Resources;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.test.AndroidTestCase;
+
+public class ContactsContract_CommonDataKinds_StructuredPostalTest extends AndroidTestCase {
+
+    private Resources mResources;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mResources = mContext.getResources();
+    }
+
+    public void testGetTypeLabel() {
+        assertGetTypeLabel(StructuredPostal.TYPE_HOME);
+        assertGetTypeLabel(StructuredPostal.TYPE_OTHER);
+        assertGetTypeLabel(StructuredPostal.TYPE_WORK);
+        assertGetTypeLabel(StructuredPostal.TYPE_CUSTOM);
+        assertCustomTypeLabel("Custom Label");
+    }
+
+    private void assertGetTypeLabel(int type) {
+        int res = StructuredPostal.getTypeLabelResource(type);
+        assertTrue(res != 0);
+
+        String label = mResources.getString(res);
+        assertEquals(label, StructuredPostal.getTypeLabel(mResources, type, ""));
+    }
+
+    private void assertCustomTypeLabel(String label) {
+        int res = StructuredPostal.getTypeLabelResource(StructuredPostal.TYPE_CUSTOM);
+        assertTrue(res != 0);
+        assertEquals(label, StructuredPostal.getTypeLabel(mResources,
+                StructuredPostal.TYPE_CUSTOM, label));
+    }
+}
diff --git a/tests/tests/webkit/src/android/webkit/cts/HttpAuthHandlerTest.java b/tests/tests/webkit/src/android/webkit/cts/HttpAuthHandlerTest.java
index 6b4e93f..9a54ace 100644
--- a/tests/tests/webkit/src/android/webkit/cts/HttpAuthHandlerTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/HttpAuthHandlerTest.java
@@ -19,7 +19,7 @@
 import android.test.ActivityInstrumentationTestCase2;
 import android.webkit.HttpAuthHandler;
 import android.webkit.WebView;
-import android.webkit.cts.WaitForLoadUrl.WaitForLoadedClient;
+import android.webkit.cts.WebViewOnUiThread.WaitForLoadedClient;
 
 import dalvik.annotation.TestLevel;
 import dalvik.annotation.TestTargetClass;
@@ -126,7 +126,7 @@
                 CtsTestServer.getReasonString(HttpStatus.SC_UNAUTHORIZED), mOnUiThread.getTitle());
     }
 
-    private static class MyWebViewClient extends WaitForLoadedClient {
+    private class MyWebViewClient extends WaitForLoadedClient {
         String realm;
         boolean useHttpAuthUsernamePassword;
 
@@ -136,6 +136,7 @@
         private int mAuthCount;
 
         MyWebViewClient(boolean proceed, String user, String password) {
+            super(mOnUiThread);
             mProceed = proceed;
             mUser = user;
             mPassword = password;
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebChromeClientTest.java b/tests/tests/webkit/src/android/webkit/cts/WebChromeClientTest.java
index 0c66fac..c0a597b 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebChromeClientTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebChromeClientTest.java
@@ -25,7 +25,7 @@
 import android.webkit.WebIconDatabase;
 import android.webkit.WebSettings;
 import android.webkit.WebView;
-import android.webkit.cts.WaitForLoadUrl.WaitForProgressClient;
+import android.webkit.cts.WebViewOnUiThread.WaitForProgressClient;
 
 import dalvik.annotation.TestLevel;
 import dalvik.annotation.TestTargetClass;
@@ -77,7 +77,7 @@
         mOnUiThread.setWebChromeClient(webChromeClient);
 
         assertFalse(webChromeClient.hadOnProgressChanged());
-        mOnUiThread.loadUrl(TestHtmlConstants.HELLO_WORLD_URL);
+        mOnUiThread.loadUrlAndWaitForCompletion(TestHtmlConstants.HELLO_WORLD_URL);
 
         new PollingCheck(TEST_TIMEOUT) {
             @Override
@@ -100,7 +100,7 @@
 
         assertFalse(webChromeClient.hadOnReceivedTitle());
         String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
-        mOnUiThread.loadUrl(url);
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
 
         new PollingCheck(TEST_TIMEOUT) {
             @Override
@@ -138,7 +138,7 @@
         assertFalse(webChromeClient.hadOnReceivedIcon());
 
         String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
-        mOnUiThread.loadUrl(url);
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
 
         new PollingCheck(TEST_TIMEOUT) {
             @Override
@@ -178,7 +178,8 @@
 
         // load a page that opens a child window, requests focus for the child and sets a timeout
         // after which the child will be closed
-        mOnUiThread.loadUrl(mWebServer.getAssetUrl(TestHtmlConstants.JS_WINDOW_URL));
+        mOnUiThread.loadUrlAndWaitForCompletion(mWebServer.
+                getAssetUrl(TestHtmlConstants.JS_WINDOW_URL));
 
         new PollingCheck(TEST_TIMEOUT) {
             @Override
@@ -243,7 +244,7 @@
         assertFalse(webChromeClient.hadOnJsAlert());
 
         String url = mWebServer.getAssetUrl(TestHtmlConstants.JS_ALERT_URL);
-        mOnUiThread.loadUrl(url);
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
 
         new PollingCheck(TEST_TIMEOUT) {
             @Override
@@ -272,7 +273,7 @@
         assertFalse(webChromeClient.hadOnJsConfirm());
 
         String url = mWebServer.getAssetUrl(TestHtmlConstants.JS_CONFIRM_URL);
-        mOnUiThread.loadUrl(url);
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
 
         new PollingCheck(TEST_TIMEOUT) {
             @Override
@@ -303,7 +304,7 @@
         final String promptResult = "CTS";
         webChromeClient.setPromptResult(promptResult);
         String url = mWebServer.getAssetUrl(TestHtmlConstants.JS_PROMPT_URL);
-        mOnUiThread.loadUrl(url);
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
 
         new PollingCheck(TEST_TIMEOUT) {
             @Override
@@ -336,6 +337,10 @@
         private boolean mHadOnRequestFocus;
         private boolean mHadOnReceivedIcon;
 
+        public MockWebChromeClient() {
+            super(mOnUiThread);
+        }
+
         public void setPromptResult(String promptResult) {
             mPromptResult = promptResult;
         }
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
index c448aad..9d65200 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
@@ -25,7 +25,7 @@
 import android.webkit.WebSettings;
 import android.webkit.WebView;
 import android.webkit.WebViewClient;
-import android.webkit.cts.WaitForLoadUrl.WaitForLoadedClient;
+import android.webkit.cts.WebViewOnUiThread.WaitForLoadedClient;
 
 import dalvik.annotation.TestLevel;
 import dalvik.annotation.TestTargetClass;
@@ -95,7 +95,7 @@
         assertFalse(webViewClient.hasOnPageStartedCalled());
         assertFalse(webViewClient.hasOnLoadResourceCalled());
         assertFalse(webViewClient.hasOnPageFinishedCalled());
-        mOnUiThread.loadUrl(url);
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
 
         new PollingCheck(TEST_TIMEOUT) {
             @Override
@@ -151,7 +151,8 @@
         String url = mWebServer.getAssetUrl(TestHtmlConstants.JS_FORM_URL);
         // this loads a form, which automatically posts itself
         mOnUiThread.loadUrlAndWaitForCompletion(url);
-        Thread.sleep(1000); // allow for javascript to post the form
+        // wait for JavaScript to post the form
+        mOnUiThread.waitForLoadCompletion();
         // the URL should have changed when the form was posted
         assertFalse(url.equals(mOnUiThread.getUrl()));
         // reloading the current URL should trigger the callback
@@ -264,6 +265,10 @@
         private boolean mOnUnhandledKeyEventCalled;
         private boolean mOnScaleChangedCalled;
 
+        public MockWebViewClient() {
+            super(mOnUiThread);
+        }
+
         public boolean hasOnPageStartedCalled() {
             return mOnPageStartedCalled;
         }
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
index b95c448..f121f2a 100755
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
@@ -38,7 +38,6 @@
 import android.test.UiThreadTest;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
-import android.util.Log;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
@@ -55,8 +54,8 @@
 import android.webkit.WebView.PictureListener;
 import android.webkit.WebViewClient;
 import android.webkit.WebViewDatabase;
-import android.webkit.cts.WaitForLoadUrl.WaitForLoadedClient;
-import android.webkit.cts.WaitForLoadUrl.WaitForProgressClient;
+import android.webkit.cts.WebViewOnUiThread.WaitForLoadedClient;
+import android.webkit.cts.WebViewOnUiThread.WaitForProgressClient;
 import android.widget.LinearLayout;
 
 import dalvik.annotation.TestLevel;
@@ -79,19 +78,13 @@
 
     private WebView mWebView;
     private CtsTestServer mWebServer;
-    private boolean mIsUiThreadDone;
+    private WebViewOnUiThread mOnUiThread;
 
     public WebViewTest() {
         super("com.android.cts.stub", WebViewStubActivity.class);
     }
 
     @Override
-    public void runTestOnUiThread(Runnable runnable) throws Throwable {
-        mIsUiThreadDone = false;
-        super.runTestOnUiThread(runnable);
-    }
-
-    @Override
     protected void setUp() throws Exception {
         super.setUp();
         mWebView = getActivity().getWebView();
@@ -99,22 +92,13 @@
         if (f.exists()) {
             f.delete();
         }
-        WaitForLoadUrl.getInstance().initializeWebView(this, mWebView);
+        mOnUiThread = new WebViewOnUiThread(this, mWebView);
     }
 
     @Override
     protected void tearDown() throws Exception {
-        try {
-            runTestOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    mWebView.clearHistory();
-                    mWebView.clearCache(true);
-                }
-            });
-        } catch(Throwable t) {
-            Log.w(LOGTAG, "tearDown(): Caught exception when posting Runnable to UI thread");
-        }
+        mOnUiThread.clearHistory();
+        mOnUiThread.clearCache(true);
         if (mWebServer != null) {
             mWebServer.shutdown();
         }
@@ -217,7 +201,7 @@
         assertTrue(settings.supportZoom());
         startWebServer(false);
         String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
-        assertLoadUrlSuccessfully(url);
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
         mWebView.invokeZoomPicker();
     }
 
@@ -406,8 +390,7 @@
 
         startWebServer(false);
         String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
-        mWebView.loadUrl(url);
-        waitForLoadComplete();
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
         assertEquals(100, mWebView.getProgress());
         assertEquals(url, mWebView.getUrl());
         assertEquals(url, mWebView.getOriginalUrl());
@@ -426,39 +409,25 @@
             args = {}
         )
     })
+    @UiThreadTest
     public void testGetOriginalUrl() throws Throwable {
         startWebServer(false);
         final String finalUrl = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
         final String redirectUrl =
                 mWebServer.getRedirectingAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
 
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                assertNull(mWebView.getUrl());
-                assertNull(mWebView.getOriginalUrl());
+        assertNull(mWebView.getUrl());
+        assertNull(mWebView.getOriginalUrl());
 
-                // By default, WebView sends an intent to ask the system to
-                // handle loading a new URL. We set a WebViewClient as
-                // WebViewClient.shouldOverrideUrlLoading() returns false, so
-                // the WebView will load the new URL.
-                mWebView.setWebViewClient(new WaitForLoadedClient());
-                mWebView.setWebChromeClient(new LoadCompleteWebChromeClient());
-                mWebView.loadUrl(redirectUrl);
-            }
-        });
+        // By default, WebView sends an intent to ask the system to
+        // handle loading a new URL. We set a WebViewClient as
+        // WebViewClient.shouldOverrideUrlLoading() returns false, so
+        // the WebView will load the new URL.
+        mOnUiThread.setWebViewClient(new WaitForLoadedClient(mOnUiThread));
+        mOnUiThread.loadUrlAndWaitForCompletion(redirectUrl);
 
-        // We need to yield the UI thread to allow the callback to
-        // WebViewClient.shouldOverrideUrlLoading() to be made.
-        waitForUiThreadDone();
-
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                assertEquals(finalUrl, mWebView.getUrl());
-                assertEquals(redirectUrl, mWebView.getOriginalUrl());
-            }
-        });
+        assertEquals(finalUrl, mWebView.getUrl());
+        assertEquals(redirectUrl, mWebView.getOriginalUrl());
     }
 
     @TestTargetNew(
@@ -526,17 +495,17 @@
         String url2 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2);
         String url3 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL3);
 
-        assertLoadUrlSuccessfully(url1);
+        mOnUiThread.loadUrlAndWaitForCompletion(url1);
         pollingCheckWebBackForwardList(url1, 0, 1);
         assertGoBackOrForwardBySteps(false, -1);
         assertGoBackOrForwardBySteps(false, 1);
 
-        assertLoadUrlSuccessfully(url2);
+        mOnUiThread.loadUrlAndWaitForCompletion(url2);
         pollingCheckWebBackForwardList(url2, 1, 2);
         assertGoBackOrForwardBySteps(true, -1);
         assertGoBackOrForwardBySteps(false, 1);
 
-        assertLoadUrlSuccessfully(url3);
+        mOnUiThread.loadUrlAndWaitForCompletion(url3);
         pollingCheckWebBackForwardList(url3, 2, 3);
         assertGoBackOrForwardBySteps(true, -2);
         assertGoBackOrForwardBySteps(false, 1);
@@ -608,7 +577,7 @@
 
         startWebServer(false);
         String url = mWebServer.getAssetUrl(TestHtmlConstants.ADD_JAVA_SCRIPT_INTERFACE_URL);
-        assertLoadUrlSuccessfully(url);
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
         assertEquals("Original title", obj.waitForResult());
     }
 
@@ -625,27 +594,27 @@
                 "<body onload=\"document.title = typeof window.injectedObject;\"></body></html>";
 
         // Test that the property is initially undefined.
-        mWebView.loadData(setTitleToPropertyTypeHtml, "text/html", null);
-        waitForLoadComplete();
+        mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml,
+                "text/html", null);
         assertEquals("undefined", mWebView.getTitle());
 
         // Test that adding a null object has no effect.
         mWebView.addJavascriptInterface(null, "injectedObject");
-        mWebView.loadData(setTitleToPropertyTypeHtml, "text/html", null);
-        waitForLoadComplete();
+        mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml,
+                "text/html", null);
         assertEquals("undefined", mWebView.getTitle());
 
         // Test that adding an object gives an object type.
         final Object obj = new Object();
         mWebView.addJavascriptInterface(obj, "injectedObject");
-        mWebView.loadData(setTitleToPropertyTypeHtml, "text/html", null);
-        waitForLoadComplete();
+        mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml,
+                "text/html", null);
         assertEquals("object", mWebView.getTitle());
 
         // Test that trying to replace with a null object has no effect.
         mWebView.addJavascriptInterface(null, "injectedObject");
-        mWebView.loadData(setTitleToPropertyTypeHtml, "text/html", null);
-        waitForLoadComplete();
+        mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml,
+                "text/html", null);
         assertEquals("object", mWebView.getTitle());
     }
 
@@ -680,13 +649,13 @@
                     "</head><body onload=\"updateTitle();\"></body></html>";
 
             mWebView.addJavascriptInterface(obj, name);
-            mWebView.loadData(Uri.encode(setTitleToPropertyTypeHtml), "text/html", null);
-            waitForLoadComplete();
+            mOnUiThread.loadDataAndWaitForCompletion(
+                    Uri.encode(setTitleToPropertyTypeHtml), "text/html", null);
             assertEquals("object", mWebView.getTitle());
 
             mWebView.removeJavascriptInterface(name);
-            mWebView.loadData(Uri.encode(setTitleToPropertyTypeHtml), "text/html", null);
-            waitForLoadComplete();
+            mOnUiThread.loadDataAndWaitForCompletion(
+                    Uri.encode(setTitleToPropertyTypeHtml), "text/html", null);
             assertEquals("undefined", mWebView.getTitle());
         }
     }
@@ -705,14 +674,14 @@
 
         // Test that adding an object gives an object type.
         mWebView.addJavascriptInterface(new Object(), "injectedObject");
-        mWebView.loadData(setTitleToPropertyTypeHtml, "text/html", null);
-        waitForLoadComplete();
+        mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml,
+                "text/html", null);
         assertEquals("object", mWebView.getTitle());
 
         // Test that reloading the page after removing the object leaves the property undefined.
         mWebView.removeJavascriptInterface("injectedObject");
-        mWebView.loadData(setTitleToPropertyTypeHtml, "text/html", null);
-        waitForLoadComplete();
+        mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml,
+                "text/html", null);
         assertEquals("undefined", mWebView.getTitle());
     }
 
@@ -728,13 +697,8 @@
                 return "removedObject";
             }
             public void remove() throws Throwable {
-                runTestOnUiThread(new Runnable() {
-                    @Override
-                    public void run() {
-                        mWebView.removeJavascriptInterface("removedObject");
-                        System.gc();
-                    }
-                });
+                mOnUiThread.removeJavascriptInterface("removedObject");
+                System.gc();
             }
         }
         class ResultObject {
@@ -759,18 +723,13 @@
 
         // Test that an object is still usable if removed while the page is in use, even if we have
         // no external references to it.
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mWebView.getSettings().setJavaScriptEnabled(true);
-                mWebView.addJavascriptInterface(new RemovedObject(), "removedObject");
-                mWebView.addJavascriptInterface(resultObject, "resultObject");
-                mWebView.loadData("<html><head></head>" +
-                        "<body onload=\"window.removedObject.remove();" +
-                        "resultObject.setResult(removedObject.toString());\"></body></html>",
-                        "text/html", null);
-            }
-        });
+        mOnUiThread.getSettings().setJavaScriptEnabled(true);
+        mOnUiThread.addJavascriptInterface(new RemovedObject(), "removedObject");
+        mOnUiThread.addJavascriptInterface(resultObject, "resultObject");
+        mOnUiThread.loadDataAndWaitForCompletion("<html><head></head>" +
+                "<body onload=\"window.removedObject.remove();" +
+                "resultObject.setResult(removedObject.toString());\"></body></html>",
+                "text/html", null);
         assertEquals("removedObject", resultObject.getResult());
     }
 
@@ -794,14 +753,8 @@
     public void testCapturePicture() throws Exception, Throwable {
         startWebServer(false);
         final String url = mWebServer.getAssetUrl(TestHtmlConstants.BLANK_PAGE_URL);
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                // showing the blank page will make the picture filled with background color
-                mWebView.loadUrl(url);
-                waitForLoadComplete();
-            }
-        });
+        // showing the blank page will make the picture filled with background color
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
         getInstrumentation().waitForIdleSync();
 
         class PictureRunnable implements Runnable {
@@ -816,8 +769,7 @@
                 assertBitmapFillWithColor(b, Color.WHITE);
 
                 mWebView.setBackgroundColor(Color.CYAN);
-                mWebView.reload();
-                waitForLoadComplete();
+                mOnUiThread.reloadAndWaitForCompletion();
             }
             public Picture getPicture() {
                 return mPicture;
@@ -860,7 +812,7 @@
             public void onNewPicture(WebView view, Picture picture) {
                 // Need to inform the listener tracking new picture
                 // for the "page loaded" knowledge since it has been replaced.
-                WaitForLoadUrl.getInstance().onNewPicture();
+                mOnUiThread.onNewPicture();
                 this.callCount += 1;
                 this.webView = view;
                 this.picture = picture;
@@ -870,13 +822,8 @@
         final MyPictureListener listener = new MyPictureListener();
         startWebServer(false);
         final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mWebView.setPictureListener(listener);
-                assertLoadUrlSuccessfully(url);
-            }
-        });
+        mOnUiThread.setPictureListener(listener);
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
         new PollingCheck(TEST_TIMEOUT) {
             @Override
             protected boolean check() {
@@ -888,12 +835,7 @@
 
         final int oldCallCount = listener.callCount;
         final String newUrl = mWebServer.getAssetUrl(TestHtmlConstants.SMALL_IMG_URL);
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                assertLoadUrlSuccessfully(newUrl);
-            }
-        });
+        mOnUiThread.loadUrlAndWaitForCompletion(newUrl);
         new PollingCheck(TEST_TIMEOUT) {
             @Override
             protected boolean check() {
@@ -924,12 +866,7 @@
         mWebView.setBackgroundColor(Color.CYAN);
         startWebServer(false);
         final String url = mWebServer.getAssetUrl(TestHtmlConstants.BLANK_PAGE_URL);
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                assertLoadUrlSuccessfully(url);
-            }
-        });
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
         getInstrumentation().waitForIdleSync();
 
         final Bundle bundle = new Bundle();
@@ -941,12 +878,7 @@
         try {
             assertTrue(bundle.isEmpty());
             assertEquals(0, f.length());
-            runTestOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    assertTrue(mWebView.savePicture(bundle, f));
-                }
-            });
+            assertTrue(mOnUiThread.savePicture(bundle, f));
 
             // File saving is done in a separate thread.
             new PollingCheck() {
@@ -963,14 +895,8 @@
             p.draw(new Canvas(b));
             assertBitmapFillWithColor(b, Color.CYAN);
 
-            runTestOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    mWebView.setBackgroundColor(Color.WHITE);
-                    mWebView.reload();
-                    waitForLoadComplete();
-                }
-            });
+            mOnUiThread.setBackgroundColor(Color.WHITE);
+            mOnUiThread.reloadAndWaitForCompletion();
             getInstrumentation().waitForIdleSync();
 
             runTestOnUiThread(new Runnable() {
@@ -1115,22 +1041,23 @@
         )
     })
     public void testLoadData() throws Throwable {
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                assertNull(mWebView.getTitle());
-                mWebView.loadData("<html><head><title>Hello,World!</title></head><body></body>" +
-                        "</html>",
-                        "text/html", null);
-                waitForLoadComplete();
-                assertEquals("Hello,World!", mWebView.getTitle());
-            }
-        });
+        final String HTML_CONTENT =
+                "<html><head><title>Hello,World!</title></head><body></body>" +
+                "</html>";
+        assertNull(mOnUiThread.getTitle());
+        mOnUiThread.loadDataAndWaitForCompletion(HTML_CONTENT,
+                "text/html", null);
+        assertEquals("Hello,World!", mOnUiThread.getTitle());
 
         // Test that JavaScript can't access cross-origin content.
         class ConsoleMessageWebChromeClient extends WaitForProgressClient {
             private boolean mIsMessageLevelAvailable;
             private ConsoleMessage.MessageLevel mMessageLevel;
+
+            public ConsoleMessageWebChromeClient() {
+                super(mOnUiThread);
+            }
+
             @Override
             public synchronized boolean onConsoleMessage(ConsoleMessage message) {
                 mMessageLevel = message.messageLevel();
@@ -1155,8 +1082,9 @@
             @Override
             public void run() {
                 mWebView.getSettings().setJavaScriptEnabled(true);
-                mWebView.setWebChromeClient(webChromeClient);
-                mWebView.loadData("<html><head></head><body onload=\"" +
+                mOnUiThread.setWebChromeClient(webChromeClient);
+                mOnUiThread.loadDataAndWaitForCompletion(
+                        "<html><head></head><body onload=\"" +
                         "document.title = " +
                         "document.getElementById('frame').contentWindow.location.href;" +
                         "\"><iframe id=\"frame\" src=\"" + crossOriginUrl + "\"></body></html>",
@@ -1193,38 +1121,36 @@
         startWebServer(false);
         String baseUrl = mWebServer.getAssetUrl("foo.html");
         String historyUrl = "random";
-        mWebView.loadDataWithBaseURL(baseUrl,
+        mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(baseUrl,
                 "<html><body><img src=\"" + imgUrl + "\"/></body></html>",
                 "text/html", "UTF-8", historyUrl);
-        waitForLoadComplete();
         assertTrue(mWebServer.getLastRequestUrl().endsWith(imgUrl));
         assertEquals(historyUrl, mWebView.getUrl());
 
         // Check that reported URL is "about:blank" when supplied history URL
         // is null.
         imgUrl = TestHtmlConstants.LARGE_IMG_URL;
-        mWebView.loadDataWithBaseURL(baseUrl,
+        mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(baseUrl,
                 "<html><body><img src=\"" + imgUrl + "\"/></body></html>",
                 "text/html", "UTF-8", null);
-        waitForLoadComplete();
         assertTrue(mWebServer.getLastRequestUrl().endsWith(imgUrl));
         assertEquals("about:blank", mWebView.getUrl());
 
         // Test that JavaScript can access content from the same origin as the base URL.
         mWebView.getSettings().setJavaScriptEnabled(true);
         final String crossOriginUrl = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
-        mWebView.loadDataWithBaseURL(baseUrl, "<html><head></head><body onload=\"" +
+        mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(baseUrl,
+                "<html><head></head><body onload=\"" +
                 "document.title = document.getElementById('frame').contentWindow.location.href;" +
                 "\"><iframe id=\"frame\" src=\"" + crossOriginUrl + "\"></body></html>",
                 "text/html", "UTF-8", null);
-        waitForLoadComplete();
         assertEquals(crossOriginUrl, mWebView.getTitle());
 
         // Check that when the base URL uses the 'data' scheme, a 'data' scheme URL is used and the
         // history URL is ignored.
-        mWebView.loadDataWithBaseURL("data:foo", "<html><body>bar</body></html>",
-                "text/html", "UTF-8", historyUrl);
-        waitForLoadComplete();
+        mOnUiThread.loadDataWithBaseURLAndWaitForCompletion("data:foo",
+                "<html><body>bar</body></html>", "text/html", "UTF-8",
+                historyUrl);
         assertTrue(mWebView.getUrl().indexOf("data:text/html,") == 0);
         assertTrue(mWebView.getUrl().indexOf("bar") > 0);
     }
@@ -1239,8 +1165,8 @@
     public void testFindAll() {
         String p = "<p>Find all instances of find on the page and highlight them.</p>";
 
-        mWebView.loadData("<html><body>" + p + "</body></html>", "text/html", null);
-        waitForLoadComplete();
+        mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
+                + "</body></html>", "text/html", null);
 
         assertEquals(2, mWebView.findAll("find"));
     }
@@ -1263,99 +1189,72 @@
         )
     })
     public void testFindNext() throws Throwable {
-        final ScrollRunnable runnable = new ScrollRunnable();
-
         final class StopScrollingPollingCheck extends PollingCheck {
             private int mPreviousScrollY = -1;
             @Override
             protected boolean check() {
                 getInstrumentation().waitForIdleSync();
-                try {
-                    runTestOnUiThread(runnable);
-                } catch (Throwable t) {}
+                int scrollY = mOnUiThread.getScrollY();
                 boolean hasStopped =
-                    (mPreviousScrollY == -1 ? false : (runnable.getScrollY() == mPreviousScrollY));
-                mPreviousScrollY = runnable.getScrollY();
+                    (mPreviousScrollY == -1 ? false : (scrollY == mPreviousScrollY));
+                mPreviousScrollY = scrollY;
                 return hasStopped;
             }
         }
 
-        final class FindNextRunnable implements Runnable {
-            private boolean mForward;
-            FindNextRunnable(boolean forward) {
-                mForward = forward;
-            }
-            @Override
-            public void run() {
-                mWebView.findNext(mForward);
-            }
-        }
+        // Reset the scaling so that finding the next "all" text will require scrolling.
+        mOnUiThread.setInitialScale(100);
 
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                // Reset the scaling so that finding the next "all" text will require scrolling.
-                mWebView.setInitialScale(100);
+        DisplayMetrics metrics = mOnUiThread.getDisplayMetrics();
+        int dimension = Math.max(metrics.widthPixels, metrics.heightPixels);
+        // create a paragraph high enough to take up the entire screen
+        String p = "<p style=\"height:" + dimension + "px;\">" +
+                "Find all instances of a word on the page and highlight them.</p>";
 
-                DisplayMetrics metrics = mWebView.getContext().getResources().getDisplayMetrics();
-                int dimension = Math.max(metrics.widthPixels, metrics.heightPixels);
-                // create a paragraph high enough to take up the entire screen
-                String p = "<p style=\"height:" + dimension + "px;\">" +
-                        "Find all instances of a word on the page and highlight them.</p>";
+        mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p + p + "</body></html>", "text/html", null);
 
-                mWebView.loadData("<html><body>" + p + p + "</body></html>", "text/html", null);
-                waitForLoadComplete();
-
-                // highlight all the strings found
-                mWebView.findAll("all");
-            }
-        });
+        // highlight all the strings found
+        mOnUiThread.findAll("all");
         getInstrumentation().waitForIdleSync();
 
-        runTestOnUiThread(runnable);
-        int previousScrollY = runnable.getScrollY();
+        int previousScrollY = mOnUiThread.getScrollY();
 
         // Focus "all" in the second page and assert that the view scrolls.
-        runTestOnUiThread(new FindNextRunnable(true));
+        mOnUiThread.findNext(true);
         new StopScrollingPollingCheck().run();
-        assertTrue(runnable.getScrollY() > previousScrollY);
-        previousScrollY = runnable.getScrollY();
+        assertTrue(mOnUiThread.getScrollY() > previousScrollY);
+        previousScrollY = mOnUiThread.getScrollY();
 
         // Focus "all" in the first page and assert that the view scrolls.
-        runTestOnUiThread(new FindNextRunnable(true));
+        mOnUiThread.findNext(true);
         new StopScrollingPollingCheck().run();
-        assertTrue(runnable.getScrollY() < previousScrollY);
-        previousScrollY = runnable.getScrollY();
+        assertTrue(mOnUiThread.getScrollY() < previousScrollY);
+        previousScrollY = mOnUiThread.getScrollY();
 
         // Focus "all" in the second page and assert that the view scrolls.
-        runTestOnUiThread(new FindNextRunnable(false));
+        mOnUiThread.findNext(false);
         new StopScrollingPollingCheck().run();
-        assertTrue(runnable.getScrollY() > previousScrollY);
-        previousScrollY = runnable.getScrollY();
+        assertTrue(mOnUiThread.getScrollY() > previousScrollY);
+        previousScrollY = mOnUiThread.getScrollY();
 
         // Focus "all" in the first page and assert that the view scrolls.
-        runTestOnUiThread(new FindNextRunnable(false));
+        mOnUiThread.findNext(false);
         new StopScrollingPollingCheck().run();
-        assertTrue(runnable.getScrollY() < previousScrollY);
-        previousScrollY = runnable.getScrollY();
+        assertTrue(mOnUiThread.getScrollY() < previousScrollY);
+        previousScrollY = mOnUiThread.getScrollY();
 
         // clear the result
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mWebView.clearMatches();
-            }
-        });
+        mOnUiThread.clearMatches();
         getInstrumentation().waitForIdleSync();
 
         // can not scroll any more
-        runTestOnUiThread(new FindNextRunnable(false));
+        mOnUiThread.findNext(false);
         new StopScrollingPollingCheck().run();
-        assertTrue(runnable.getScrollY() == previousScrollY);
+        assertTrue(mOnUiThread.getScrollY() == previousScrollY);
 
-        runTestOnUiThread(new FindNextRunnable(true));
+        mOnUiThread.findNext(true);
         new StopScrollingPollingCheck().run();
-        assertTrue(runnable.getScrollY() == previousScrollY);
+        assertTrue(mOnUiThread.getScrollY() == previousScrollY);
     }
 
     @TestTargetNew(
@@ -1395,9 +1294,8 @@
         runTestOnUiThread(new Runnable() {
             @Override
             public void run() {
-                mWebView.loadData("<html><body><img src=\"" + imgUrl + "\"/></body></html>",
-                        "text/html", null);
-                waitForLoadComplete();
+                mOnUiThread.loadDataAndWaitForCompletion("<html><body><img src=\""
+                        + imgUrl + "\"/></body></html>", "text/html", null);
                 Message response = new Message();
                 response.setTarget(handler);
                 assertFalse(handler.hasCalledHandleMessage());
@@ -1426,97 +1324,41 @@
         )
     })
     public void testPageScroll() throws Throwable {
-        final class PageUpRunnable implements Runnable {
-            private boolean mResult;
-            @Override
-            public void run() {
-                mResult = mWebView.pageUp(false);
-            }
-            public boolean getResult() {
-                return mResult;
-            }
-        }
-
-        final class PageDownRunnable implements Runnable {
-            private boolean mResult;
-            @Override
-            public void run() {
-                mResult = mWebView.pageDown(false);
-            }
-            public boolean getResult() {
-                return mResult;
-            }
-        }
-
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                DisplayMetrics metrics = mWebView.getContext().getResources().getDisplayMetrics();
-                int dimension = 2 * Math.max(metrics.widthPixels, metrics.heightPixels);
-                String p = "<p style=\"height:" + dimension + "px;\">" +
-                        "Scroll by half the size of the page.</p>";
-                mWebView.loadData("<html><body>" + p + p + "</body></html>", "text/html", null);
-                waitForLoadComplete();
-            }
-        });
+        DisplayMetrics metrics = mOnUiThread.getDisplayMetrics();
+        int dimension = 2 * Math.max(metrics.widthPixels, metrics.heightPixels);
+        String p = "<p style=\"height:" + dimension + "px;\">" +
+                "Scroll by half the size of the page.</p>";
+        mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
+                + p + "</body></html>", "text/html", null);
         getInstrumentation().waitForIdleSync();
 
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                assertTrue(mWebView.pageDown(false));
-            }
-        });
+        assertTrue(mOnUiThread.pageDown(false));
 
-        PageDownRunnable pageDownRunnable = new PageDownRunnable();
         do {
             getInstrumentation().waitForIdleSync();
-            runTestOnUiThread(pageDownRunnable);
-        } while (pageDownRunnable.getResult());
+        } while (mOnUiThread.pageDown(false));
 
-        ScrollRunnable scrollRunnable = new ScrollRunnable();
         getInstrumentation().waitForIdleSync();
-        runTestOnUiThread(scrollRunnable);
-        int bottomScrollY = scrollRunnable.getScrollY();
+        int bottomScrollY = mOnUiThread.getScrollY();
 
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                assertTrue(mWebView.pageUp(false));
-            }
-        });
+        assertTrue(mOnUiThread.pageUp(false));
 
-        PageUpRunnable pageUpRunnable = new PageUpRunnable();
         do {
             getInstrumentation().waitForIdleSync();
-            runTestOnUiThread(pageUpRunnable);
-        } while (pageUpRunnable.getResult());
+        } while (mOnUiThread.pageUp(false));
 
         getInstrumentation().waitForIdleSync();
-        runTestOnUiThread(scrollRunnable);
-        int topScrollY = scrollRunnable.getScrollY();
+        int topScrollY = mOnUiThread.getScrollY();
 
         // jump to the bottom
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                assertTrue(mWebView.pageDown(true));
-            }
-        });
+        assertTrue(mOnUiThread.pageDown(true));
         getInstrumentation().waitForIdleSync();
-        runTestOnUiThread(scrollRunnable);
-        assertEquals(bottomScrollY, scrollRunnable.getScrollY());
+        assertEquals(bottomScrollY, mOnUiThread.getScrollY());
 
         // jump to the top
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                assertTrue(mWebView.pageUp(true));
-            }
-        });
+        assertTrue(mOnUiThread.pageUp(true));
         getInstrumentation().waitForIdleSync();
-        runTestOnUiThread(scrollRunnable);
-        assertEquals(topScrollY, scrollRunnable.getScrollY());
+        assertEquals(topScrollY, mOnUiThread.getScrollY());
     }
 
     @TestTargetNew(
@@ -1525,60 +1367,27 @@
         args = {}
     )
     public void testGetContentHeight() throws Throwable {
-        final class HeightRunnable implements Runnable {
-            private int mHeight;
-            private int mContentHeight;
-            @Override
-            public void run() {
-                mHeight = mWebView.getHeight();
-                mContentHeight = mWebView.getContentHeight();
-            }
-            public int getHeight() {
-                return mHeight;
-            }
-            public int getContentHeight() {
-                return mContentHeight;
-            }
-        }
-
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mWebView.loadData("<html><body></body></html>", "text/html", null);
-                waitForLoadComplete();
-            }
-        });
+        mOnUiThread.loadDataAndWaitForCompletion(
+                "<html><body></body></html>", "text/html", null);
         getInstrumentation().waitForIdleSync();
 
         final int pageHeight = 600;
         // set the margin to 0
         final String p = "<p style=\"height:" + pageHeight
                 + "px;margin:0px auto;\">Get the height of HTML content.</p>";
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                assertEquals(mWebView.getHeight(), mWebView.getContentHeight() * mWebView.getScale(), 2f);
-                mWebView.loadData("<html><body>" + p + "</body></html>", "text/html", null);
-                waitForLoadComplete();
-            }
-        });
+        assertEquals(mOnUiThread.getHeight(), mOnUiThread.getContentHeight() * mOnUiThread.getScale(), 2f);
+        mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
+                + "</body></html>", "text/html", null);
         getInstrumentation().waitForIdleSync();
 
-        HeightRunnable runnable = new HeightRunnable();
-        runTestOnUiThread(runnable);
-        assertTrue(runnable.getContentHeight() > pageHeight);
-        int extraSpace = runnable.getContentHeight() - pageHeight;
+        assertTrue(mOnUiThread.getContentHeight() > pageHeight);
+        int extraSpace = mOnUiThread.getContentHeight() - pageHeight;
 
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mWebView.loadData("<html><body>" + p + p + "</body></html>", "text/html", null);
-                waitForLoadComplete();
-            }
-        });
+        mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
+                + p + "</body></html>", "text/html", null);
         getInstrumentation().waitForIdleSync();
-        runTestOnUiThread(runnable);
-        assertEquals(pageHeight + pageHeight + extraSpace, runnable.getContentHeight());
+        assertEquals(pageHeight + pageHeight + extraSpace,
+                mOnUiThread.getContentHeight());
     }
 
     @TestTargetNew(
@@ -1594,8 +1403,7 @@
 
         startWebServer(false);
         final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
-        mWebView.loadUrl(url);
-        waitForLoadComplete();
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
         new PollingCheck(TEST_TIMEOUT) {
             @Override
             protected boolean check() {
@@ -1679,50 +1487,35 @@
         args = {int.class, int.class}
     )
     public void testFlingScroll() throws Throwable {
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                DisplayMetrics metrics = mWebView.getContext().getResources().getDisplayMetrics();
-                int dimension = 2 * Math.max(metrics.widthPixels, metrics.heightPixels);
-                String p = "<p style=\"height:" + dimension + "px;" +
-                        "width:" + dimension + "px\">Test fling scroll.</p>";
-                mWebView.loadData("<html><body>" + p + "</body></html>", "text/html", null);
-                waitForLoadComplete();
-            }
-        });
+        DisplayMetrics metrics = mOnUiThread.getDisplayMetrics();
+        int dimension = 2 * Math.max(metrics.widthPixels, metrics.heightPixels);
+        String p = "<p style=\"height:" + dimension + "px;" +
+                "width:" + dimension + "px\">Test fling scroll.</p>";
+        mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
+                + "</body></html>", "text/html", null);
         getInstrumentation().waitForIdleSync();
 
-        ScrollRunnable runnable = new ScrollRunnable();
-        runTestOnUiThread(runnable);
-        int previousScrollX = runnable.getScrollX();
-        int previousScrollY = runnable.getScrollY();
+        int previousScrollX = mOnUiThread.getScrollX();
+        int previousScrollY = mOnUiThread.getScrollY();
 
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mWebView.flingScroll(100, 100);
-            }
-        });
+        mOnUiThread.flingScroll(100, 100);
 
         int timeSlice = 500;
         Thread.sleep(timeSlice);
-        runTestOnUiThread(runnable);
-        assertTrue(runnable.getScrollX() > previousScrollX);
-        assertTrue(runnable.getScrollY() > previousScrollY);
+        assertTrue(mOnUiThread.getScrollX() > previousScrollX);
+        assertTrue(mOnUiThread.getScrollY() > previousScrollY);
 
-        previousScrollY = runnable.getScrollY();
-        previousScrollX = runnable.getScrollX();
+        previousScrollY = mOnUiThread.getScrollY();
+        previousScrollX = mOnUiThread.getScrollX();
         Thread.sleep(timeSlice);
-        runTestOnUiThread(runnable);
-        assertTrue(runnable.getScrollX() >= previousScrollX);
-        assertTrue(runnable.getScrollY() >= previousScrollY);
+        assertTrue(mOnUiThread.getScrollX() >= previousScrollX);
+        assertTrue(mOnUiThread.getScrollY() >= previousScrollY);
 
-        previousScrollY = runnable.getScrollY();
-        previousScrollX = runnable.getScrollX();
+        previousScrollY = mOnUiThread.getScrollY();
+        previousScrollX = mOnUiThread.getScrollX();
         Thread.sleep(timeSlice);
-        runTestOnUiThread(runnable);
-        assertTrue(runnable.getScrollX() >= previousScrollX);
-        assertTrue(runnable.getScrollY() >= previousScrollY);
+        assertTrue(mOnUiThread.getScrollX() >= previousScrollX);
+        assertTrue(mOnUiThread.getScrollY() >= previousScrollY);
     }
 
     @TestTargetNew(
@@ -1734,13 +1527,7 @@
         final String links = "<DL><p><DT><A HREF=\"" + TestHtmlConstants.HTML_URL1
                 + "\">HTML_URL1</A><DT><A HREF=\"" + TestHtmlConstants.HTML_URL2
                 + "\">HTML_URL2</A></DL><p>";
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mWebView.loadData("<html><body>" + links + "</body></html>", "text/html", null);
-                waitForLoadComplete();
-            }
-        });
+        mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + links + "</body></html>", "text/html", null);
         getInstrumentation().waitForIdleSync();
 
         final HrefCheckHandler handler = new HrefCheckHandler(mWebView.getHandler().getLooper());
@@ -1750,12 +1537,7 @@
         // focus on first link
         handler.reset();
         getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_DOWN);
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mWebView.requestFocusNodeHref(hrefMsg);
-            }
-        });
+        mOnUiThread.requestFocusNodeHref(hrefMsg);
         new PollingCheck() {
             @Override
             protected boolean check() {
@@ -1769,12 +1551,7 @@
         final Message hrefMsg2 = new Message();
         hrefMsg2.setTarget(handler);
         getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_DOWN);
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mWebView.requestFocusNodeHref(hrefMsg2);
-            }
-        });
+        mOnUiThread.requestFocusNodeHref(hrefMsg2);
         new PollingCheck() {
             @Override
             protected boolean check() {
@@ -1783,12 +1560,7 @@
         }.run();
         assertEquals(TestHtmlConstants.HTML_URL2, handler.getResultUrl());
 
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mWebView.requestFocusNodeHref(null);
-            }
-        });
+        mOnUiThread.requestFocusNodeHref(null);
     }
 
     @TestTargetNew(
@@ -1797,18 +1569,6 @@
         args = {android.os.Message.class}
     )
     public void testRequestImageRef() throws Exception, Throwable {
-        final class GetLocationRunnable implements Runnable {
-            private int[] mLocation;
-            @Override
-            public void run() {
-                mLocation = new int[2];
-                mWebView.getLocationOnScreen(mLocation);
-            }
-            public int[] getLocation() {
-                return mLocation;
-            }
-        }
-
         AssetManager assets = getActivity().getAssets();
         Bitmap bitmap = BitmapFactory.decodeStream(assets.open(TestHtmlConstants.LARGE_IMG_URL));
         int imgWidth = bitmap.getWidth();
@@ -1816,14 +1576,9 @@
 
         startWebServer(false);
         final String imgUrl = mWebServer.getAssetUrl(TestHtmlConstants.LARGE_IMG_URL);
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mWebView.loadData("<html><title>Title</title><body><img src=\"" + imgUrl
-                        + "\"/></body></html>", "text/html", null);
-                waitForLoadComplete();
-            }
-        });
+        mOnUiThread.loadDataAndWaitForCompletion(
+                "<html><title>Title</title><body><img src=\"" + imgUrl
+                + "\"/></body></html>", "text/html", null);
         getInstrumentation().waitForIdleSync();
 
         final HrefCheckHandler handler = new HrefCheckHandler(mWebView.getHandler().getLooper());
@@ -1832,9 +1587,7 @@
 
         // touch the image
         handler.reset();
-        GetLocationRunnable runnable = new GetLocationRunnable();
-        runTestOnUiThread(runnable);
-        int[] location = runnable.getLocation();
+        int[] location = mOnUiThread.getLocationOnScreen();
 
         long time = SystemClock.uptimeMillis();
         getInstrumentation().sendPointerSync(
@@ -1842,12 +1595,7 @@
                         location[0] + imgWidth / 2,
                         location[1] + imgHeight / 2, 0));
         getInstrumentation().waitForIdleSync();
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mWebView.requestImageRef(msg);
-            }
-        });
+        mOnUiThread.requestImageRef(msg);
         new PollingCheck() {
             @Override
             protected boolean check() {
@@ -1873,17 +1621,6 @@
         args = {}
     )
     public void testGetHitTestResult() throws Throwable {
-        class HitTestResultRunnable implements Runnable {
-            private HitTestResult mHitTestResult;
-            @Override
-            public void run() {
-                mHitTestResult = mWebView.getHitTestResult();
-            }
-            public HitTestResult getHitTestResult() {
-                return mHitTestResult;
-            }
-        }
-
         final String anchor = "<p><a href=\"" + TestHtmlConstants.EXT_WEB_URL1
                 + "\">normal anchor</a></p>";
         final String blankAnchor = "<p><a href=\"\">blank anchor</a></p>";
@@ -1896,58 +1633,52 @@
         String location = "shanghai";
         final String geo = "<p><a href=\"geo:0,0?q=" + location + "\">Location</a></p>";
 
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mWebView.loadDataWithBaseURL("fake://home", "<html><body>" + anchor + blankAnchor + form
-                        + tel + mailto + geo + "</body></html>", "text/html", "UTF-8", null);
-                waitForLoadComplete();
-            }
-        });
+        mOnUiThread.loadDataWithBaseURLAndWaitForCompletion("fake://home",
+                "<html><body>" + anchor + blankAnchor + form + tel + mailto +
+                geo + "</body></html>", "text/html", "UTF-8", null);
         getInstrumentation().waitForIdleSync();
-        HitTestResultRunnable runnable = new HitTestResultRunnable();
 
         // anchor
         moveFocusDown();
-        runTestOnUiThread(runnable);
-        assertEquals(HitTestResult.SRC_ANCHOR_TYPE, runnable.getHitTestResult().getType());
-        assertEquals(TestHtmlConstants.EXT_WEB_URL1, runnable.getHitTestResult().getExtra());
+        HitTestResult hitTestResult = mOnUiThread.getHitTestResult();
+        assertEquals(HitTestResult.SRC_ANCHOR_TYPE, hitTestResult.getType());
+        assertEquals(TestHtmlConstants.EXT_WEB_URL1, hitTestResult.getExtra());
 
         // blank anchor
         moveFocusDown();
-        runTestOnUiThread(runnable);
-        assertEquals(HitTestResult.SRC_ANCHOR_TYPE, runnable.getHitTestResult().getType());
-        assertEquals("fake://home", runnable.getHitTestResult().getExtra());
+        hitTestResult = mOnUiThread.getHitTestResult();
+        assertEquals(HitTestResult.SRC_ANCHOR_TYPE, hitTestResult.getType());
+        assertEquals("fake://home", hitTestResult.getExtra());
 
         // text field
         moveFocusDown();
-        runTestOnUiThread(runnable);
-        assertEquals(HitTestResult.EDIT_TEXT_TYPE, runnable.getHitTestResult().getType());
-        assertNull(runnable.getHitTestResult().getExtra());
+        hitTestResult = mOnUiThread.getHitTestResult();
+        assertEquals(HitTestResult.EDIT_TEXT_TYPE, hitTestResult.getType());
+        assertNull(hitTestResult.getExtra());
 
         // submit button
         moveFocusDown();
-        runTestOnUiThread(runnable);
-        assertEquals(HitTestResult.UNKNOWN_TYPE, runnable.getHitTestResult().getType());
-        assertNull(runnable.getHitTestResult().getExtra());
+        hitTestResult = mOnUiThread.getHitTestResult();
+        assertEquals(HitTestResult.UNKNOWN_TYPE, hitTestResult.getType());
+        assertNull(hitTestResult.getExtra());
 
         // phone number
         moveFocusDown();
-        runTestOnUiThread(runnable);
-        assertEquals(HitTestResult.PHONE_TYPE, runnable.getHitTestResult().getType());
-        assertEquals(phoneNo, runnable.getHitTestResult().getExtra());
+        hitTestResult = mOnUiThread.getHitTestResult();
+        assertEquals(HitTestResult.PHONE_TYPE, hitTestResult.getType());
+        assertEquals(phoneNo, hitTestResult.getExtra());
 
         // email
         moveFocusDown();
-        runTestOnUiThread(runnable);
-        assertEquals(HitTestResult.EMAIL_TYPE, runnable.getHitTestResult().getType());
-        assertEquals(email, runnable.getHitTestResult().getExtra());
+        hitTestResult = mOnUiThread.getHitTestResult();
+        assertEquals(HitTestResult.EMAIL_TYPE, hitTestResult.getType());
+        assertEquals(email, hitTestResult.getExtra());
 
         // geo address
         moveFocusDown();
-        runTestOnUiThread(runnable);
-        assertEquals(HitTestResult.GEO_TYPE, runnable.getHitTestResult().getType());
-        assertEquals(location, runnable.getHitTestResult().getExtra());
+        hitTestResult = mOnUiThread.getHitTestResult();
+        assertEquals(HitTestResult.GEO_TYPE, hitTestResult.getType());
+        assertEquals(location, hitTestResult.getExtra());
     }
 
     @TestTargetNew(
@@ -1960,58 +1691,33 @@
         final float defaultScale =
             getInstrumentation().getTargetContext().getResources().getDisplayMetrics().density;
 
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mWebView.loadData("<html><body>" + p + "</body></html>", "text/html", null);
-                waitForLoadComplete();
-            }
-        });
+        mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
+                + "</body></html>", "text/html", null);
         getInstrumentation().waitForIdleSync();
 
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                assertEquals(defaultScale, mWebView.getScale(), .01f);
+        assertEquals(defaultScale, mOnUiThread.getScale(), .01f);
 
-                mWebView.setInitialScale(0);
-                // modify content to fool WebKit into re-loading
-                mWebView.loadData("<html><body>" + p + "2" + "</body></html>", "text/html", null);
-                waitForLoadComplete();
-            }
-        });
+        mOnUiThread.setInitialScale(0);
+        // modify content to fool WebKit into re-loading
+        mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
+                + "2" + "</body></html>", "text/html", null);
         getInstrumentation().waitForIdleSync();
 
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                assertEquals(defaultScale, mWebView.getScale(), .01f);
+        assertEquals(defaultScale, mOnUiThread.getScale(), .01f);
 
-                mWebView.setInitialScale(50);
-                mWebView.loadData("<html><body>" + p + "3" + "</body></html>", "text/html", null);
-                waitForLoadComplete();
-            }
-        });
+        mOnUiThread.setInitialScale(50);
+        mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
+                + "3" + "</body></html>", "text/html", null);
         getInstrumentation().waitForIdleSync();
 
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                assertEquals(0.5f, mWebView.getScale(), .02f);
+        assertEquals(0.5f, mOnUiThread.getScale(), .02f);
 
-                mWebView.setInitialScale(0);
-                mWebView.loadData("<html><body>" + p + "4" + "</body></html>", "text/html", null);
-                waitForLoadComplete();
-            }
-        });
+        mOnUiThread.setInitialScale(0);
+        mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
+                + "4" + "</body></html>", "text/html", null);
         getInstrumentation().waitForIdleSync();
 
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                assertEquals(defaultScale, mWebView.getScale(), .01f);
-            }
-        });
+        assertEquals(defaultScale, mOnUiThread.getScale(), .01f);
     }
 
     @TestTargetNew(
@@ -2025,7 +1731,7 @@
     public void testGetFavicon() throws Exception {
         startWebServer(false);
         String url = mWebServer.getAssetUrl(TestHtmlConstants.TEST_FAVICON_URL);
-        assertLoadUrlSuccessfully(url);
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
         mWebView.getFavicon();
         // ToBeFixed: Favicon is not loaded automatically.
         // assertNotNull(mWebView.getFavicon());
@@ -2043,13 +1749,13 @@
         String url2 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2);
         String url3 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL3);
 
-        assertLoadUrlSuccessfully(url1);
+        mOnUiThread.loadUrlAndWaitForCompletion(url1);
         pollingCheckWebBackForwardList(url1, 0, 1);
 
-        assertLoadUrlSuccessfully(url2);
+        mOnUiThread.loadUrlAndWaitForCompletion(url2);
         pollingCheckWebBackForwardList(url2, 1, 2);
 
-        assertLoadUrlSuccessfully(url3);
+        mOnUiThread.loadUrlAndWaitForCompletion(url3);
         pollingCheckWebBackForwardList(url3, 2, 3);
 
         mWebView.clearHistory();
@@ -2087,11 +1793,11 @@
         String url3 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL3);
 
         // make a history list
-        assertLoadUrlSuccessfully(url1);
+        mOnUiThread.loadUrlAndWaitForCompletion(url1);
         pollingCheckWebBackForwardList(url1, 0, 1);
-        assertLoadUrlSuccessfully(url2);
+        mOnUiThread.loadUrlAndWaitForCompletion(url2);
         pollingCheckWebBackForwardList(url2, 1, 2);
-        assertLoadUrlSuccessfully(url3);
+        mOnUiThread.loadUrlAndWaitForCompletion(url3);
         pollingCheckWebBackForwardList(url3, 2, 3);
 
         // save the list
@@ -2148,6 +1854,9 @@
     public void testSetWebViewClient() throws Throwable {
         final class MockWebViewClient extends WaitForLoadedClient {
             private boolean mOnScaleChangedCalled = false;
+            public MockWebViewClient() {
+                super(mOnUiThread);
+            }
             @Override
             public void onScaleChanged(WebView view, float oldScale, float newScale) {
                 super.onScaleChanged(view, oldScale, newScale);
@@ -2159,21 +1868,11 @@
         }
 
         final MockWebViewClient webViewClient = new MockWebViewClient();
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mWebView.setWebViewClient(webViewClient);
-            }
-        });
+        mOnUiThread.setWebViewClient(webViewClient);
         getInstrumentation().waitForIdleSync();
         assertFalse(webViewClient.onScaleChangedCalled());
 
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mWebView.zoomIn();
-            }
-        });
+        mOnUiThread.zoomIn();
         getInstrumentation().waitForIdleSync();
         assertTrue(webViewClient.onScaleChangedCalled());
     }
@@ -2210,32 +1909,15 @@
             args = {}
         )
     })
+    @UiThreadTest
     public void testInsecureSiteClearsCertificate() throws Throwable {
         final SslCertificate certificate =
                 new SslCertificate("foo", "bar", new Date(42), new Date(43));
         startWebServer(false);
         final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mWebView.setWebChromeClient(new LoadCompleteWebChromeClient());
-                mWebView.setCertificate(certificate);
-                mWebView.loadUrl(url);
-            }
-        });
-        waitForUiThreadDone();
-
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                new PollingCheck(TEST_TIMEOUT) {
-                    @Override
-                    protected boolean check() {
-                        return mWebView.getCertificate() == null;
-                    }
-                }.run();
-            }
-        });
+        mWebView.setCertificate(certificate);
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        assertNull(mWebView.getCertificate());
     }
 
     @TestTargets({
@@ -2250,8 +1932,12 @@
             args = {}
         )
     })
+    @UiThreadTest
     public void testSecureSiteSetsCertificate() throws Throwable {
         final class MockWebViewClient extends WaitForLoadedClient {
+            public MockWebViewClient() {
+                super(mOnUiThread);
+            }
             @Override
             public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
                 handler.proceed();
@@ -2260,31 +1946,12 @@
 
         startWebServer(true);
         final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mWebView.setWebViewClient(new MockWebViewClient());
-                mWebView.setWebChromeClient(new LoadCompleteWebChromeClient());
-                mWebView.setCertificate(null);
-                mWebView.loadUrl(url);
-            }
-        });
-        waitForUiThreadDone();
-
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                new PollingCheck(TEST_TIMEOUT) {
-                    @Override
-                    protected boolean check() {
-                        return mWebView.getCertificate() != null;
-                    }
-                }.run();
-                SslCertificate cert = mWebView.getCertificate();
-                assertNotNull(cert);
-                assertEquals("Android", cert.getIssuedTo().getUName());
-            }
-        });
+        mOnUiThread.setWebViewClient(new MockWebViewClient());
+        mWebView.setCertificate(null);
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        SslCertificate cert = mWebView.getCertificate();
+        assertNotNull(cert);
+        assertEquals("Android", cert.getIssuedTo().getUName());
     }
 
     @TestTargetNew(
@@ -2307,6 +1974,10 @@
         final class MockWebViewClient extends WaitForLoadedClient {
             private String mErrorUrl;
             private WebView mWebView;
+
+            public MockWebViewClient() {
+                super(mOnUiThread);
+            }
             @Override
             public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
                 mWebView = view;
@@ -2324,16 +1995,9 @@
         startWebServer(true);
         final String errorUrl = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
         final MockWebViewClient webViewClient = new MockWebViewClient();
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mWebView.setWebViewClient(webViewClient);
-                mWebView.setWebChromeClient(new LoadCompleteWebChromeClient());
-                mWebView.clearSslPreferences();
-                mWebView.loadUrl(errorUrl);
-            }
-        });
-        waitForUiThreadDone();
+        mOnUiThread.setWebViewClient(webViewClient);
+        mOnUiThread.clearSslPreferences();
+        mOnUiThread.loadUrlAndWaitForCompletion(errorUrl);
 
         assertEquals(mWebView, webViewClient.webView());
         assertEquals(errorUrl, webViewClient.errorUrl());
@@ -2346,6 +2010,9 @@
     )
     public void testOnReceivedSslErrorProceed() throws Throwable {
         final class MockWebViewClient extends WaitForLoadedClient {
+            public MockWebViewClient() {
+                super(mOnUiThread);
+            }
             @Override
             public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
                 handler.proceed();
@@ -2354,21 +2021,9 @@
 
         startWebServer(true);
         final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mWebView.setWebViewClient(new MockWebViewClient());
-                mWebView.setWebChromeClient(new LoadCompleteWebChromeClient());
-                mWebView.loadUrl(url);
-            }
-        });
-        waitForUiThreadDone();
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                assertEquals(TestHtmlConstants.HELLO_WORLD_TITLE, mWebView.getTitle());
-            }
-        });
+        mOnUiThread.setWebViewClient(new MockWebViewClient());
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        assertEquals(TestHtmlConstants.HELLO_WORLD_TITLE, mOnUiThread.getTitle());
     }
 
     @TestTargetNew(
@@ -2378,6 +2033,9 @@
     )
     public void testOnReceivedSslErrorCancel() throws Throwable {
         final class MockWebViewClient extends WaitForLoadedClient {
+            public MockWebViewClient() {
+                super(mOnUiThread);
+            }
             @Override
             public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
                 handler.cancel();
@@ -2386,22 +2044,10 @@
 
         startWebServer(true);
         final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mWebView.setWebViewClient(new MockWebViewClient());
-                mWebView.setWebChromeClient(new LoadCompleteWebChromeClient());
-                mWebView.clearSslPreferences();
-                mWebView.loadUrl(url);
-            }
-        });
-        waitForUiThreadDone();
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                assertFalse(TestHtmlConstants.HELLO_WORLD_TITLE.equals(mWebView.getTitle()));
-            }
-        });
+        mOnUiThread.setWebViewClient(new MockWebViewClient());
+        mOnUiThread.clearSslPreferences();
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        assertFalse(TestHtmlConstants.HELLO_WORLD_TITLE.equals(mOnUiThread.getTitle()));
     }
 
     @TestTargetNew(
@@ -2415,36 +2061,18 @@
         final SslErrorWebViewClient webViewClient = new SslErrorWebViewClient();
         startWebServer(true);
         final String firstUrl = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1);
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mWebView.setWebViewClient(webViewClient);
-                mWebView.setWebChromeClient(new LoadCompleteWebChromeClient());
-                mWebView.clearSslPreferences();
-                mWebView.loadUrl(firstUrl);
-            }
-        });
-        waitForUiThreadDone();
+        mOnUiThread.setWebViewClient(webViewClient);
+        mOnUiThread.clearSslPreferences();
+        mOnUiThread.loadUrlAndWaitForCompletion(firstUrl);
         assertTrue(webViewClient.wasOnReceivedSslErrorCalled());
 
         // Load the second page. We don't expect a call to
         // WebViewClient.onReceivedSslError(), but the page should load.
         webViewClient.resetWasOnReceivedSslErrorCalled();
         final String sameHostUrl = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2);
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mWebView.loadUrl(sameHostUrl);
-            }
-        });
-        waitForUiThreadDone();
+        mOnUiThread.loadUrlAndWaitForCompletion(sameHostUrl);
         assertFalse(webViewClient.wasOnReceivedSslErrorCalled());
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                assertEquals("Second page", mWebView.getTitle());
-            }
-        });
+        assertEquals("Second page", mOnUiThread.getTitle());
     }
 
     @TestTargetNew(
@@ -2458,16 +2086,9 @@
         final SslErrorWebViewClient webViewClient = new SslErrorWebViewClient();
         startWebServer(true);
         final String firstUrl = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1);
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mWebView.setWebViewClient(webViewClient);
-                mWebView.setWebChromeClient(new LoadCompleteWebChromeClient());
-                mWebView.clearSslPreferences();
-                mWebView.loadUrl(firstUrl);
-            }
-        });
-        waitForUiThreadDone();
+        mOnUiThread.setWebViewClient(webViewClient);
+        mOnUiThread.clearSslPreferences();
+        mOnUiThread.loadUrlAndWaitForCompletion(firstUrl);
         assertTrue(webViewClient.wasOnReceivedSslErrorCalled());
 
         // Load the second page. We expect another call to
@@ -2477,20 +2098,9 @@
         // alias, but will be considered unique by the WebView.
         final String differentHostUrl = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2).replace(
                 "localhost", "127.0.0.1");
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mWebView.loadUrl(differentHostUrl);
-            }
-        });
-        waitForUiThreadDone();
+        mOnUiThread.loadUrlAndWaitForCompletion(differentHostUrl);
         assertTrue(webViewClient.wasOnReceivedSslErrorCalled());
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                assertEquals("Second page", mWebView.getTitle());
-            }
-        });
+        assertEquals("Second page", mOnUiThread.getTitle());
     }
 
     @TestTargetNew(
@@ -2499,33 +2109,23 @@
         args = {View.class, Rect.class, boolean.class}
     )
     public void testRequestChildRectangleOnScreen() throws Throwable {
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                DisplayMetrics metrics = mWebView.getContext().getResources().getDisplayMetrics();
-                final int dimension = 2 * Math.max(metrics.widthPixels, metrics.heightPixels);
-                String p = "<p style=\"height:" + dimension + "px;width:" + dimension + "px\">&nbsp;</p>";
-                mWebView.loadData("<html><body>" + p + "</body></html>", "text/html", null);
-                waitForLoadComplete();
-            }
-        });
+        DisplayMetrics metrics = mOnUiThread.getDisplayMetrics();
+        int dimension = 2 * Math.max(metrics.widthPixels, metrics.heightPixels);
+        String p = "<p style=\"height:" + dimension + "px;width:" + dimension + "px\">&nbsp;</p>";
+        mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
+                + "</body></html>", "text/html", null);
         getInstrumentation().waitForIdleSync();
 
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                int origX = mWebView.getScrollX();
-                int origY = mWebView.getScrollY();
+        int origX = mOnUiThread.getScrollX();
+        int origY = mOnUiThread.getScrollY();
 
-                DisplayMetrics metrics = mWebView.getContext().getResources().getDisplayMetrics();
-                final int dimension = 2 * Math.max(metrics.widthPixels, metrics.heightPixels);
-                int half = dimension / 2;
-                Rect rect = new Rect(half, half, half + 1, half + 1);
-                assertTrue(mWebView.requestChildRectangleOnScreen(mWebView, rect, true));
-                assertTrue(mWebView.getScrollX() > origX);
-                assertTrue(mWebView.getScrollY() > origY);
-            }
-        });
+        metrics = mOnUiThread.getDisplayMetrics();
+        dimension = 2 * Math.max(metrics.widthPixels, metrics.heightPixels);
+        int half = dimension / 2;
+        Rect rect = new Rect(half, half, half + 1, half + 1);
+        assertTrue(mOnUiThread.requestChildRectangleOnScreen(mWebView, rect, true));
+        assertTrue(mOnUiThread.getScrollX() > origX);
+        assertTrue(mOnUiThread.getScrollY() > origY);
     }
 
     @TestTargets({
@@ -2567,28 +2167,18 @@
         startWebServer(false);
         final String url = mWebServer.getBinaryUrl(mimeType, length);
 
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                // By default, WebView sends an intent to ask the system to
-                // handle loading a new URL. We set WebViewClient as
-                // WebViewClient.shouldOverrideUrlLoading() returns false, so
-                // the WebView will load the new URL.
-                mWebView.setWebViewClient(new WaitForLoadedClient());
-                mWebView.setDownloadListener(listener);
-                mWebView.loadData("<html><body><a href=\"" + url + "\">link</a></body></html>",
-                        "text/html", null);
-                waitForLoadComplete();
-            }
-        });
+        // By default, WebView sends an intent to ask the system to
+        // handle loading a new URL. We set WebViewClient as
+        // WebViewClient.shouldOverrideUrlLoading() returns false, so
+        // the WebView will load the new URL.
+        mOnUiThread.setDownloadListener(listener);
+        mOnUiThread.loadDataAndWaitForCompletion(
+                "<html><body><a href=\"" + url
+                + "\">link</a></body></html>",
+                "text/html", null);
         // Wait for layout to complete before setting focus.
         getInstrumentation().waitForIdleSync();
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                assertTrue(mWebView.requestFocus(View.FOCUS_DOWN, null));
-            }
-        });
+        assertTrue(mOnUiThread.requestFocus(View.FOCUS_DOWN, null));
         getInstrumentation().waitForIdleSync();
         getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_CENTER);
         new PollingCheck(TEST_TIMEOUT) {
@@ -2639,17 +2229,15 @@
         settings.setJavaScriptEnabled(true);
         startWebServer(false);
         String url = mWebServer.getAssetUrl(TestHtmlConstants.NETWORK_STATE_URL);
-        assertLoadUrlSuccessfully(url);
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
         assertEquals("ONLINE", mWebView.getTitle());
 
         mWebView.setNetworkAvailable(false);
-        mWebView.reload();
-        waitForLoadComplete();
+        mOnUiThread.reloadAndWaitForCompletion();
         assertEquals("OFFLINE", mWebView.getTitle());
 
         mWebView.setNetworkAvailable(true);
-        mWebView.reload();
-        waitForLoadComplete();
+        mOnUiThread.reloadAndWaitForCompletion();
         assertEquals("ONLINE", mWebView.getTitle());
     }
 
@@ -2661,6 +2249,11 @@
     public void testSetWebChromeClient() throws Throwable {
         final class MockWebChromeClient extends WaitForProgressClient {
             private boolean mOnProgressChanged = false;
+
+            public MockWebChromeClient() {
+                super(mOnUiThread);
+            }
+
             @Override
             public void onProgressChanged(WebView view, int newProgress) {
                 super.onProgressChanged(view, newProgress);
@@ -2673,23 +2266,13 @@
 
         final MockWebChromeClient webChromeClient = new MockWebChromeClient();
 
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mWebView.setWebChromeClient(webChromeClient);
-            }
-        });
+        mOnUiThread.setWebChromeClient(webChromeClient);
         getInstrumentation().waitForIdleSync();
         assertFalse(webChromeClient.onProgressChangedCalled());
 
         startWebServer(false);
         final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mWebView.loadUrl(url);
-            }
-        });
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
         getInstrumentation().waitForIdleSync();
 
         new PollingCheck(TEST_TIMEOUT) {
@@ -2737,7 +2320,7 @@
             }
         };
         final Monitor monitor = new Monitor();
-        final String updateMonitorHtml = "<html><head></head>" +
+        final String updateMonitorHtml = "<html>" +
                 "<body onload=\"monitor.update();\"></body></html>";
 
         // Test that JavaScript is executed even with timers paused.
@@ -2747,27 +2330,20 @@
                 mWebView.getSettings().setJavaScriptEnabled(true);
                 mWebView.addJavascriptInterface(monitor, "monitor");
                 mWebView.pauseTimers();
-                mWebView.loadData(updateMonitorHtml, "text/html", null);
+                mOnUiThread.loadDataAndWaitForCompletion(updateMonitorHtml,
+                        "text/html", null);
             }
         });
         assertTrue(monitor.waitForUpdate());
 
         // Start a timer and test that it does not fire.
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mWebView.loadUrl("javascript:setTimeout(function(){monitor.update();},100)");
-            }
-        });
+        mOnUiThread.loadDataAndWaitForCompletion(
+                "<html><body onload='setTimeout(function(){monitor.update();},100)'>" +
+                "</body></html>", "text/html", null);
         assertFalse(monitor.waitForUpdate());
 
         // Resume timers and test that the timer fires.
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mWebView.resumeTimers();
-            }
-        });
+        mOnUiThread.resumeTimers();
         assertTrue(monitor.waitForUpdate());
     }
 
@@ -2909,22 +2485,6 @@
         Thread.sleep(500);
     }
 
-    private final class ScrollRunnable implements Runnable {
-        private int mScrollX;
-        private int mScrollY;
-        @Override
-        public void run() {
-            mScrollX = mWebView.getScrollX();
-            mScrollY = mWebView.getScrollY();
-        }
-        public int getScrollX() {
-            return mScrollX;
-        }
-        public int getScrollY() {
-            return mScrollY;
-        }
-    }
-
     private void pollingCheckWebBackForwardList(final String currUrl, final int currIndex,
             final int size) {
         new PollingCheck() {
@@ -3017,46 +2577,13 @@
         return true;
     }
 
-    private void assertLoadUrlSuccessfully(String url) {
-        mWebView.loadUrl(url);
-        waitForLoadComplete();
-    }
-
-    private void waitForLoadComplete() {
-        WaitForLoadUrl.getInstance().waitForLoadComplete(mWebView);
-    }
-
-    private synchronized void notifyUiThreadDone() {
-        mIsUiThreadDone = true;
-        notify();
-    }
-
-    private synchronized void waitForUiThreadDone() throws InterruptedException {
-        while (!mIsUiThreadDone) {
-            try {
-                wait(TEST_TIMEOUT);
-            } catch (InterruptedException e) {
-                continue;
-            }
-            if (!mIsUiThreadDone) {
-                Assert.fail("Unexpected timeout");
-            }
-        }
-    }
-
-    final class LoadCompleteWebChromeClient extends WaitForProgressClient {
-        @Override
-        public void onProgressChanged(WebView webView, int progress) {
-            super.onProgressChanged(webView, progress);
-            if (progress == 100) {
-                notifyUiThreadDone();
-            }
-        }
-    }
-
     // Note that this class is not thread-safe.
     final class SslErrorWebViewClient extends WaitForLoadedClient {
         private boolean mWasOnReceivedSslErrorCalled;
+
+        public SslErrorWebViewClient() {
+            super(mOnUiThread);
+        }
         @Override
         public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
             mWasOnReceivedSslErrorCalled = true;
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebView_WebViewTransportTest.java b/tests/tests/webkit/src/android/webkit/cts/WebView_WebViewTransportTest.java
index 4bebf84..bbe1fd3 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebView_WebViewTransportTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebView_WebViewTransportTest.java
@@ -17,6 +17,7 @@
 package android.webkit.cts;
 
 import android.test.AndroidTestCase;
+import android.test.UiThreadTest;
 import android.webkit.WebView;
 import android.webkit.WebView.WebViewTransport;
 
@@ -39,6 +40,7 @@
             args = {}
         )
     })
+    @UiThreadTest
     public void testAccessWebView() {
         WebView webView = new WebView(mContext);
         WebViewTransport transport = webView.new WebViewTransport();