Merge "New WordIterator tests"
diff --git a/CtsHostLibraryList.mk b/CtsHostLibraryList.mk
index 36291d5..ac9a700 100644
--- a/CtsHostLibraryList.mk
+++ b/CtsHostLibraryList.mk
@@ -12,5 +12,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-CTS_HOST_LIBRARY_JARS := \
-	$(HOST_OUT_JAVA_LIBRARIES)/CtsTestAnnotationsHostLib.jar
+CTS_HOST_LIBRARY_JARS := 
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 24e9fdc..e104d82 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -69,32 +69,23 @@
             <meta-data android:name="test_category" android:value="@string/test_category_sensors" />
         </activity>
 
-        <activity android:name=".sensors.MagnetometerTestActivity" android:label="@string/snsr_mag_test"
-                android:screenOrientation="nosensor">
+        <activity android:name=".audioquality.AudioQualityVerifierActivity"
+                android:label="@string/aq_verifier">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.cts.intent.category.MANUAL_TEST" />
             </intent-filter>
-            <meta-data android:name="test_category" android:value="@string/test_category_sensors" />
+            <meta-data android:name="test_category" android:value="@string/test_category_audio" />
         </activity>
 
-      <activity android:name=".audioquality.AudioQualityVerifierActivity"
-              android:label="@string/aq_verifier">
-          <intent-filter>
-              <action android:name="android.intent.action.MAIN" />
-              <category android:name="android.cts.intent.category.MANUAL_TEST" />
-          </intent-filter>
-          <meta-data android:name="test_category" android:value="@string/test_category_audio" />
-      </activity>
-      
-      <activity android:name=".audioquality.CalibrateVolumeActivity"
-                android:label="@string/aq_calibrate_volume_name" />
-      
-      <activity android:name=".audioquality.ViewResultsActivity"
-                android:label="@string/aq_view_results_name" />
+        <activity android:name=".audioquality.CalibrateVolumeActivity"
+                  android:label="@string/aq_calibrate_volume_name" />
 
-      <service android:name=".audioquality.ExperimentService" />
-      
+        <activity android:name=".audioquality.ViewResultsActivity"
+                  android:label="@string/aq_view_results_name" />
+
+        <service android:name=".audioquality.ExperimentService" />
+
    </application>
 
 </manifest> 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagnetometerTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagnetometerTestActivity.java
deleted file mode 100644
index f3ba411..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagnetometerTestActivity.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.sensors;
-
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-import android.content.Context;
-import android.hardware.Sensor;
-import android.hardware.SensorManager;
-import android.opengl.GLSurfaceView;
-import android.os.Bundle;
-
-/**
- * CTS Verifier case for verifying correct integration of accelerometer.
- * Displays a wedge using OpenGL that, on a correctly-integrated device, always
- * points down.
- */
-public class MagnetometerTestActivity extends PassFailButtons.Activity {
-    private GLSurfaceView mGLSurfaceView;
-
-    private AccelerometerTestRenderer mListener;
-
-    private SensorManager mSensorManager;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        mSensorManager = (SensorManager) getApplicationContext().getSystemService(
-                Context.SENSOR_SERVICE);
-        AccelerometerTestRenderer renderer = new MagnetometerTestRenderer(this);
-        mListener = renderer;
-
-        setContentView(R.layout.pass_fail_gl);
-        setPassFailButtonClickListeners();
-        setInfoResources(R.string.snsr_mag_test, R.string.snsr_mag_test_info, -1);
-        mGLSurfaceView = (GLSurfaceView) findViewById(R.id.gl_surface_view);
-        mGLSurfaceView.setRenderer(renderer);
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-        mSensorManager.unregisterListener(mListener);
-        mGLSurfaceView.onPause();
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        mSensorManager.registerListener(mListener, mSensorManager.getSensorList(
-                Sensor.TYPE_MAGNETIC_FIELD).get(0), SensorManager.SENSOR_DELAY_UI);
-        mGLSurfaceView.onResume();
-    }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagnetometerTestRenderer.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagnetometerTestRenderer.java
deleted file mode 100644
index b5d4587..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagnetometerTestRenderer.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.sensors;
-
-import android.content.Context;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-
-public class MagnetometerTestRenderer extends AccelerometerTestRenderer {
-    public MagnetometerTestRenderer(Context context) {
-        super(context);
-    }
-
-    @Override
-    public void onSensorChanged(SensorEvent event) {
-        if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
-            /*
-             * The base class is written for accelerometer, where the vector
-             * points *away* from the thing being measured (i.e. gravity). But
-             * our vector points *toward* the thing being measured (i.e.
-             * magnetic north pole). Accordingly, the base class has an
-             * inversion to handle that that doesn't apply to us, so the
-             * simplest method is just to flip our vector to point in the exact
-             * opposite direction and then everything works out in the base
-             * class.
-             */
-            event.values[0] *= -1;
-            event.values[1] *= -1;
-            event.values[2] *= -1;
-
-            // rest of method is the same as in base class
-            normalize(event.values);
-            event.values[1] *= -1;
-            crossProduct(event.values, Z_AXIS, mCrossProd);
-            mAngle = (float) Math.acos(dotProduct(event.values, Z_AXIS));
-        }
-    }
-}
diff --git a/development/ide/eclipse/.classpath b/development/ide/eclipse/.classpath
index ee4a413..8d10955 100644
--- a/development/ide/eclipse/.classpath
+++ b/development/ide/eclipse/.classpath
@@ -2,7 +2,6 @@
 <classpath>
     <classpathentry kind="src" path="cts/apps/CtsVerifier/src"/>
     <classpathentry kind="src" path="cts/apps/CtsVerifier/tests/src"/>
-    <classpathentry kind="src" path="cts/libs/annotation/src"/>
     <classpathentry kind="src" path="cts/libs/vogar-expect/src"/>
     <classpathentry kind="src" path="cts/tests/ApiDemosReferenceTest/src"/>
     <classpathentry kind="src" path="cts/tests/ProcessTest/src"/>
@@ -59,7 +58,6 @@
     <classpathentry kind="src" path="cts/tools/host/test"/>
     <classpathentry kind="src" path="cts/tools/signature-tools/src"/>
     <classpathentry kind="src" path="cts/tools/signature-tools/test"/>
-    <classpathentry kind="src" path="cts/tools/spec-progress/src"/>
     <classpathentry kind="src" path="cts/tools/utils"/>
     <classpathentry kind="src" path="cts/tools/vm-tests/src"/>
     <classpathentry kind="src" path="out/target/common/obj/APPS/CtsAccessibilityServiceTestCases_intermediates/src/src"/>
diff --git a/libs/annotation/Android.mk b/libs/annotation/Android.mk
deleted file mode 100644
index 3a4dd82..0000000
--- a/libs/annotation/Android.mk
+++ /dev/null
@@ -1,30 +0,0 @@
-#
-# Copyright (C) 2010 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-# Build static Java library for APKs like the CTS tests. 
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_MODULE := CtsTestAnnotationsLib
-LOCAL_MODULE_TAGS := optional
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# Build the annotations for the host to use.
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_MODULE := CtsTestAnnotationsHostLib
-LOCAL_MODULE_TAGS := optional
-include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/libs/annotation/src/android/annotation/cts/RequiredFeatures.java b/libs/annotation/src/android/annotation/cts/RequiredFeatures.java
deleted file mode 100644
index 8675b34..0000000
--- a/libs/annotation/src/android/annotation/cts/RequiredFeatures.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.annotation.cts;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Annotation for specifying what features are needed to run a test. The device must have all the
- * features specified by the annotation in order to be executed.
- * <p>
- * Examples:
- * <pre>
- * @RequiredFeatures(PackageManager.FEATURE_WIFI)
- * @RequiredFeatures({PackageManager.FEATURE_TELEPHONY, PackageManager.FEATURE_TELEPHONY_CDMA})
- * </pre>
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.TYPE, ElementType.METHOD})
-public @interface RequiredFeatures {
-    String[] value();
-}
diff --git a/tests/Android.mk b/tests/Android.mk
index d3ecabe..641d053 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -24,7 +24,6 @@
               src/android/os/cts/IEmptyService.aidl
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := CtsTestAnnotationsLib
 
 LOCAL_JNI_SHARED_LIBRARIES := libcts_jni
 
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 6f302a6..c881e28 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -653,6 +653,14 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="android.webkit.cts.WebDriverStubActivity"
+            android:label="WebDriverStubActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+            </intent-filter>
+        </activity>
+
         <activity android:name="android.app.cts.ChildActivity"
                         android:label="ChildActivity" />
 
@@ -943,6 +951,13 @@
         <activity android:name="android.app.cts.ActivityManagerMemoryClassTestActivity"
                 android:process=":memoryclass" />
 
+        <service android:name="android.speech.tts.cts.StubTextToSpeechService">
+            <intent-filter>
+                <action android:name="android.intent.action.TTS_SERVICE" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </service>
+
     </application>
 
     <!--Test for PackageManager, please put this at the very beginning-->
diff --git a/tests/assets/webkit/form_page.html b/tests/assets/webkit/form_page.html
new file mode 100644
index 0000000..a1fe48a
--- /dev/null
+++ b/tests/assets/webkit/form_page.html
@@ -0,0 +1,87 @@
+<!-- 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.
+-->
+
+<html>
+<head>
+    <title>Test Page</title>
+</head>
+<body>
+  <a id="tagA">Tag A</a>
+  <a name="text"></a>
+  <a id=someTextId>Some text&nbsp;</a>
+  <a id=nestedLinkId>Is not nested, and is not unique!</a>
+  <a class="divClass">Outter text</a>
+  <a id="outter" name="foo">Foo</a>
+
+  <div id=divId name="firstDiv">A div
+    <a id=nestedLinkId class="divClass" name="nestedLink">Nested text</a>
+    <a id="n1" name="linkn1">a nested link</a>
+    <a id="inner" name="foo">Foo</a>
+    <span id="n1" name="spann1"></span>
+    <a name=nested>Nested text</a>
+  </div>
+  <p id="spaces">     </p>
+  <p id="empty"></p>
+  <a name=foo href="foo" id="linkWithEqualsSign">Link=equalssign</a>
+  <p id="self-closed" />Here is some content that should not be in the previous p tag
+
+  <p class=" spaceAround ">Spaced out</p>
+
+  <a name="text" id="emptyLink"> </a>
+  <p id="spaces">     </p>
+  <p id="empty"></p>
+  <p id="self-closed"/>
+
+  <div>
+  <a id="id1" href="#">Foo</a>
+  <ul id="id2" />
+  <span id="id3"/>
+  <p id="id3">A paragraph</p>
+  </div>
+
+  <input name="inputDisabled" id="inputDisabled" disabled="disabled"> </input>
+  <span id="my_span">
+    <div>first_div</div>
+    <div>second_div</div>
+    <span>first_span</span>
+    <span>second_span</span>
+    <a id="Foo">Foo</a>
+  </span>
+
+  <select name="selectomatic">
+          <option id="one" selected="selected">One</option>
+          <option id="two">Two</option>
+          <option id="four">Four</option>
+          <option>Still learning how to count, apparently</option>
+  </select>
+
+  <select name="multi" id="multi" multiple="multiple">
+         <option id="eggs" selected="selected">Eggs</option>
+         <option id="ham" >Ham</option>
+         <option selected="selected">Sausages</option>
+         <option>Onion gravy</option>
+  </select>
+
+  <input type="radio" id="cheese" name="snack" value="cheese"/>Cheese<br/>
+  <input type="radio" id="peas" name="snack" value="peas"/>Peas<br/>
+  <input type="radio" id="cheese_and_peas" name="snack" value="cheese and peas" checked/>Cheese and peas<br/>
+  <input type="radio" id="nothing" name="snack" value="nowt" disabled="disabled"/>Not a sausage
+
+  <input type="hidden" name="hidden" value="fromage" />
+  Here's a checkbox: <input type="checkbox" id="checky" name="checky" value="furrfu"/><br/>
+
+
+</body>
+</html>
diff --git a/tests/core/ctscore.mk b/tests/core/ctscore.mk
index 7131e82..11fc253 100644
--- a/tests/core/ctscore.mk
+++ b/tests/core/ctscore.mk
@@ -18,7 +18,6 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
 LOCAL_JAVA_LIBRARIES := android.test.runner bouncycastle
-LOCAL_STATIC_JAVA_LIBRARIES += CtsTestAnnotationsLib
 
 LOCAL_PROGUARD_ENABLED := disabled
 
diff --git a/tests/core/runner/src/android/test/InstrumentationCtsTestRunner.java b/tests/core/runner/src/android/test/InstrumentationCtsTestRunner.java
index 21eb2a6..5aa2c3b 100644
--- a/tests/core/runner/src/android/test/InstrumentationCtsTestRunner.java
+++ b/tests/core/runner/src/android/test/InstrumentationCtsTestRunner.java
@@ -22,11 +22,8 @@
 import dalvik.annotation.BrokenTest;
 import dalvik.annotation.SideEffect;
 
-import android.annotation.cts.RequiredFeatures;
-import android.app.Instrumentation;
 import android.app.KeyguardManager;
 import android.content.Context;
-import android.content.pm.FeatureInfo;
 import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.test.suitebuilder.TestMethod;
@@ -36,10 +33,7 @@
 import java.io.File;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
-import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 import java.util.TimeZone;
 
 import junit.framework.AssertionFailedError;
@@ -67,8 +61,6 @@
 
     private static final String REPORT_VALUE_ID = "InstrumentationCtsTestRunner";
 
-    private static final int REPORT_VALUE_RESULT_OMITTED = -3;
-
     /**
      * True if (and only if) we are running in single-test mode (as opposed to
      * batch mode).
@@ -235,8 +227,6 @@
                 Predicates.not(new HasAnnotation(BrokenTest.class));
         builderRequirements.add(brokenTestPredicate);
 
-        builderRequirements.add(getFeaturePredicate());
-
         if (!mSingleTest) {
             Predicate<TestMethod> sideEffectPredicate =
                     Predicates.not(new HasAnnotation(SideEffect.class));
@@ -244,64 +234,4 @@
         }
         return builderRequirements;
     }
-
-    /**
-     * Send back an indication that a test was omitted. InstrumentationTestRunner won't run omitted
-     * tests, but CTS needs to know that the test was omitted. Otherwise, it will attempt to rerun
-     * the test thinking that ADB must have crashed or something.
-     */
-    private void sendOmittedStatus(TestMethod t) {
-        Bundle bundle = new Bundle();
-        bundle.putString(Instrumentation.REPORT_KEY_IDENTIFIER, REPORT_VALUE_ID);
-        bundle.putInt(InstrumentationTestRunner.REPORT_KEY_NUM_TOTAL, 1);
-        bundle.putInt(InstrumentationTestRunner.REPORT_KEY_NUM_CURRENT, 1);
-        bundle.putString(InstrumentationTestRunner.REPORT_KEY_NAME_CLASS,
-                t.getEnclosingClassname());
-        bundle.putString(InstrumentationTestRunner.REPORT_KEY_NAME_TEST,
-                t.getName());
-
-        // First status message causes CTS to print out the test name like "Class#test..."
-        sendStatus(InstrumentationTestRunner.REPORT_VALUE_RESULT_START, bundle);
-
-        // Second status message causes CTS to complete the line like "Class#test...(omitted)"
-        sendStatus(REPORT_VALUE_RESULT_OMITTED, bundle);
-    }
-
-
-    private Predicate<TestMethod> getFeaturePredicate() {
-        return new Predicate<TestMethod>() {
-            public boolean apply(TestMethod t) {
-                if (isValidTest(t)) {
-                    // InstrumentationTestRunner will run the test and send back results.
-                    return true;
-                } else {
-                    // InstrumentationTestRunner WON'T run the test, so send back omitted status.
-                    sendOmittedStatus(t);
-                    return false;
-                }
-            }
-
-            private boolean isValidTest(TestMethod t) {
-                Set<String> features = new HashSet<String>();
-                add(features, t.getAnnotation(RequiredFeatures.class));
-                add(features, t.getEnclosingClass().getAnnotation(RequiredFeatures.class));
-
-                // Run the test only if the device supports all the features.
-                PackageManager packageManager = getContext().getPackageManager();
-                FeatureInfo[] featureInfos = packageManager.getSystemAvailableFeatures();
-                if (featureInfos != null) {
-                    for (FeatureInfo featureInfo : featureInfos) {
-                        features.remove(featureInfo.name);
-                    }
-                }
-                return features.isEmpty();
-            }
-
-            private void add(Set<String> features, RequiredFeatures annotation) {
-                if (annotation != null) {
-                    Collections.addAll(features, annotation.value());
-                }
-            }
-        };
-    }
 }
diff --git a/tests/expectations/knownfailures-3.0_r1.txt b/tests/expectations/knownfailures-3.0_r1.txt
index 405729d..a13d081 100644
--- a/tests/expectations/knownfailures-3.0_r1.txt
+++ b/tests/expectations/knownfailures-3.0_r1.txt
@@ -28,6 +28,8 @@
   description: "Flakey",
   names: [
     "android.hardware.cts.CameraTest#testSetPreviewDisplay",
+    "android.view.cts.GestureDetectorTest#testOnTouchEvent",
+    "android.view.cts.ViewTest#testWindowVisibilityChanged",
     "android.webkit.cts.WebChromeClientTest#testOnReceivedIcon",
     "android.widget.cts.AbsListViewTest#testGetContextMenuInfo"
   ]
diff --git a/tests/jni/Android.mk b/tests/jni/Android.mk
index d787d68..ed4c46f 100644
--- a/tests/jni/Android.mk
+++ b/tests/jni/Android.mk
@@ -22,7 +22,8 @@
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_SRC_FILES := \
-		CtsJniOnLoad.cpp
+		CtsJniOnLoad.cpp \
+		android_os_cts_FileUtils.cpp
 
 LOCAL_C_INCLUDES := $(JNI_H_INCLUDE) 
 
diff --git a/tests/jni/CtsJniOnLoad.cpp b/tests/jni/CtsJniOnLoad.cpp
index b6177a1..d6a1d2b 100644
--- a/tests/jni/CtsJniOnLoad.cpp
+++ b/tests/jni/CtsJniOnLoad.cpp
@@ -21,6 +21,8 @@
 extern int register_android_os_cts_CpuFeatures(JNIEnv*);
 #endif
 
+extern int register_android_os_cts_FileUtils(JNIEnv*);
+
 jint JNI_OnLoad(JavaVM *vm, void *reserved) {
     JNIEnv *env = NULL;
 
@@ -34,5 +36,9 @@
     }
 #endif
 
+    if (register_android_os_cts_FileUtils(env)) {
+        return JNI_ERR;
+    }
+
     return JNI_VERSION_1_4;
 }
diff --git a/tests/jni/android_os_cts_FileUtils.cpp b/tests/jni/android_os_cts_FileUtils.cpp
new file mode 100644
index 0000000..d78d26c
--- /dev/null
+++ b/tests/jni/android_os_cts_FileUtils.cpp
@@ -0,0 +1,142 @@
+/* 
+ * 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.
+ */
+
+#include <grp.h>
+#include <jni.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+static jclass gFileStatusClass;
+static jfieldID gFileStatusDevFieldID;
+static jfieldID gFileStatusInoFieldID;
+static jfieldID gFileStatusModeFieldID;
+static jfieldID gFileStatusNlinkFieldID;
+static jfieldID gFileStatusUidFieldID;
+static jfieldID gFileStatusGidFieldID;
+static jfieldID gFileStatusSizeFieldID;
+static jfieldID gFileStatusBlksizeFieldID;
+static jfieldID gFileStatusBlocksFieldID;
+static jfieldID gFileStatusAtimeFieldID;
+static jfieldID gFileStatusMtimeFieldID;
+static jfieldID gFileStatusCtimeFieldID;
+
+/*
+ * Native methods used by
+ * cts/tests/src/android/os/cts/FileUtils.java
+ *
+ * Copied from hidden API: frameworks/base/core/jni/android_os_FileUtils.cpp
+ */
+
+jboolean android_os_cts_FileUtils_getFileStatus(JNIEnv* env, jobject thiz,
+        jstring path, jobject fileStatus, jboolean statLinks)
+{
+    const char* pathStr = env->GetStringUTFChars(path, NULL);
+    jboolean ret = false;
+    struct stat s;
+
+    int res = statLinks == true ? lstat(pathStr, &s) : stat(pathStr, &s);
+
+    if (res == 0) {
+        ret = true;
+        if (fileStatus != NULL) {
+            env->SetIntField(fileStatus, gFileStatusDevFieldID, s.st_dev);
+            env->SetIntField(fileStatus, gFileStatusInoFieldID, s.st_ino);
+            env->SetIntField(fileStatus, gFileStatusModeFieldID, s.st_mode);
+            env->SetIntField(fileStatus, gFileStatusNlinkFieldID, s.st_nlink);
+            env->SetIntField(fileStatus, gFileStatusUidFieldID, s.st_uid);
+            env->SetIntField(fileStatus, gFileStatusGidFieldID, s.st_gid);
+            env->SetLongField(fileStatus, gFileStatusSizeFieldID, s.st_size);
+            env->SetIntField(fileStatus, gFileStatusBlksizeFieldID, s.st_blksize);
+            env->SetLongField(fileStatus, gFileStatusBlocksFieldID, s.st_blocks);
+            env->SetLongField(fileStatus, gFileStatusAtimeFieldID, s.st_atime);
+            env->SetLongField(fileStatus, gFileStatusMtimeFieldID, s.st_mtime);
+            env->SetLongField(fileStatus, gFileStatusCtimeFieldID, s.st_ctime);
+        }
+    }
+
+    env->ReleaseStringUTFChars(path, pathStr);
+
+    return ret;
+}
+
+jstring android_os_cts_FileUtils_getUserName(JNIEnv* env, jobject thiz,
+        jint uid)
+{
+    struct passwd *pwd = getpwuid(uid);
+    return env->NewStringUTF(pwd->pw_name);
+}
+
+jstring android_os_cts_FileUtils_getGroupName(JNIEnv* env, jobject thiz,
+        jint gid)
+{
+    struct group *grp = getgrgid(gid);
+    return env->NewStringUTF(grp->gr_name);
+}
+
+jint android_os_cts_FileUtils_setPermissions(JNIEnv* env, jobject clazz,
+        jstring file, jint mode)
+{
+    const char *fileStr = env->GetStringUTFChars(file, NULL);
+    if (fileStr == NULL) {
+        return -1;
+    }
+
+    if (strlen(fileStr) <= 0) {
+        env->ReleaseStringUTFChars(file, fileStr);
+        return ENOENT;
+    } 
+
+    jint returnValue = chmod(fileStr, mode) == 0 ? 0 : errno;
+    env->ReleaseStringUTFChars(file, fileStr);
+    return returnValue;
+}
+
+static JNINativeMethod gMethods[] = {
+    {  "getFileStatus", "(Ljava/lang/String;Landroid/os/cts/FileUtils$FileStatus;Z)Z",
+            (void *) android_os_cts_FileUtils_getFileStatus  },
+    {  "getUserName", "(I)Ljava/lang/String;",
+            (void *) android_os_cts_FileUtils_getUserName  },
+    {  "getGroupName", "(I)Ljava/lang/String;",
+            (void *) android_os_cts_FileUtils_getGroupName  },
+    {  "setPermissions", "(Ljava/lang/String;I)I",
+            (void *) android_os_cts_FileUtils_setPermissions },
+};
+
+int register_android_os_cts_FileUtils(JNIEnv* env)
+{
+    jclass clazz = env->FindClass("android/os/cts/FileUtils");
+
+    gFileStatusClass = env->FindClass("android/os/cts/FileUtils$FileStatus");
+    gFileStatusDevFieldID = env->GetFieldID(gFileStatusClass, "dev", "I");
+    gFileStatusInoFieldID = env->GetFieldID(gFileStatusClass, "ino", "I");
+    gFileStatusModeFieldID = env->GetFieldID(gFileStatusClass, "mode", "I");
+    gFileStatusNlinkFieldID = env->GetFieldID(gFileStatusClass, "nlink", "I");
+    gFileStatusUidFieldID = env->GetFieldID(gFileStatusClass, "uid", "I");
+    gFileStatusGidFieldID = env->GetFieldID(gFileStatusClass, "gid", "I");
+    gFileStatusSizeFieldID = env->GetFieldID(gFileStatusClass, "size", "J");
+    gFileStatusBlksizeFieldID = env->GetFieldID(gFileStatusClass, "blksize", "I");
+    gFileStatusBlocksFieldID = env->GetFieldID(gFileStatusClass, "blocks", "J");
+    gFileStatusAtimeFieldID = env->GetFieldID(gFileStatusClass, "atime", "J");
+    gFileStatusMtimeFieldID = env->GetFieldID(gFileStatusClass, "mtime", "J");
+    gFileStatusCtimeFieldID = env->GetFieldID(gFileStatusClass, "ctime", "J");
+
+    return env->RegisterNatives(clazz, gMethods, 
+            sizeof(gMethods) / sizeof(JNINativeMethod)); 
+}
diff --git a/tests/res/raw/test1.obb b/tests/res/raw/test1.obb
new file mode 100644
index 0000000..a6b3e1c
--- /dev/null
+++ b/tests/res/raw/test1.obb
Binary files differ
diff --git a/tests/res/raw/test1_nosig.obb b/tests/res/raw/test1_nosig.obb
new file mode 100644
index 0000000..5c3573f
--- /dev/null
+++ b/tests/res/raw/test1_nosig.obb
Binary files differ
diff --git a/tests/res/raw/test1_wrongpackage.obb b/tests/res/raw/test1_wrongpackage.obb
new file mode 100644
index 0000000..d0aafe1
--- /dev/null
+++ b/tests/res/raw/test1_wrongpackage.obb
Binary files differ
diff --git a/tests/src/android/os/cts/FileUtils.java b/tests/src/android/os/cts/FileUtils.java
new file mode 100644
index 0000000..feaf7d7
--- /dev/null
+++ b/tests/src/android/os/cts/FileUtils.java
@@ -0,0 +1,122 @@
+/*
+ * 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.os.cts;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/** Bits and pieces copied from hidden API of android.os.FileUtils. */
+public class FileUtils {
+
+    public static final int S_IFSOCK = 0140000;
+    public static final int S_IFLNK = 0120000;
+    public static final int S_IFREG = 0100000;
+    public static final int S_IFBLK = 0060000;
+    public static final int S_IFDIR = 0040000;
+    public static final int S_IFCHR = 0020000;
+    public static final int S_IFIFO = 0010000;
+
+    public static final int S_ISUID = 0004000;
+    public static final int S_ISGID = 0002000;
+    public static final int S_ISVTX = 0001000;
+
+    public static final int S_IRWXU = 00700;
+    public static final int S_IRUSR = 00400;
+    public static final int S_IWUSR = 00200;
+    public static final int S_IXUSR = 00100;
+
+    public static final int S_IRWXG = 00070;
+    public static final int S_IRGRP = 00040;
+    public static final int S_IWGRP = 00020;
+    public static final int S_IXGRP = 00010;
+
+    public static final int S_IRWXO = 00007;
+    public static final int S_IROTH = 00004;
+    public static final int S_IWOTH = 00002;
+    public static final int S_IXOTH = 00001;
+
+    static {
+        System.loadLibrary("cts_jni");
+    }
+
+    public static class FileStatus {
+
+        public int dev;
+        public int ino;
+        public int mode;
+        public int nlink;
+        public int uid;
+        public int gid;
+        public int rdev;
+        public long size;
+        public int blksize;
+        public long blocks;
+        public long atime;
+        public long mtime;
+        public long ctime;
+
+        public boolean hasModeFlag(int flag) {
+            return (mode & flag) == flag;
+        }
+    }
+
+    /**
+     * @param path of the file to stat
+     * @param status object to set the fields on
+     * @param statLinks or don't stat links (lstat vs stat)
+     * @return whether or not we were able to stat the file
+     */
+    public native static boolean getFileStatus(String path, FileStatus status, boolean statLinks);
+
+    public native static String getUserName(int uid);
+
+    public native static String getGroupName(int gid);
+
+    public native static int setPermissions(String file, int mode);
+
+    /**
+     * Copy data from a source stream to destFile.
+     * Return true if succeed, return false if failed.
+     */
+    public static boolean copyToFile(InputStream inputStream, File destFile) {
+        try {
+            if (destFile.exists()) {
+                destFile.delete();
+            }
+            FileOutputStream out = new FileOutputStream(destFile);
+            try {
+                byte[] buffer = new byte[4096];
+                int bytesRead;
+                while ((bytesRead = inputStream.read(buffer)) >= 0) {
+                    out.write(buffer, 0, bytesRead);
+                }
+            } finally {
+                out.flush();
+                try {
+                    out.getFD().sync();
+                } catch (IOException e) {
+                }
+                out.close();
+            }
+            return true;
+        } catch (IOException e) {
+            return false;
+        }
+    }
+}
diff --git a/tests/src/android/speech/tts/cts/StubTextToSpeechService.java b/tests/src/android/speech/tts/cts/StubTextToSpeechService.java
new file mode 100644
index 0000000..ed675df
--- /dev/null
+++ b/tests/src/android/speech/tts/cts/StubTextToSpeechService.java
@@ -0,0 +1,81 @@
+/*
+ * 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.speech.tts.cts;
+
+import android.media.AudioFormat;
+import android.speech.tts.SynthesisRequest;
+import android.speech.tts.TextToSpeech;
+import android.speech.tts.TextToSpeechService;
+
+/**
+ * Stub implementation of {@link TextToSpeechService}. Used for testing the
+ * TTS engine API.
+ */
+public class StubTextToSpeechService extends TextToSpeechService {
+
+    public static final String TEXT_STREAM = "stream";
+    public static final String TEXT_COMPLETE = "complete";
+
+    @Override
+    protected String[] onGetLanguage() {
+        return new String[] { "eng", "USA", "" };
+    }
+
+    @Override
+    protected int onIsLanguageAvailable(String lang, String country, String variant) {
+        return TextToSpeech.LANG_AVAILABLE;
+    }
+
+    @Override
+    protected int onLoadLanguage(String lang, String country, String variant) {
+        return TextToSpeech.LANG_AVAILABLE;
+    }
+
+    @Override
+    protected void onStop() {
+    }
+
+    @Override
+    protected void onSynthesizeText(SynthesisRequest request) {
+        if (TEXT_STREAM.equals(request.getText())) {
+            synthesizeStreaming(request);
+        } else {
+            synthesizeComplete(request);
+        }
+    }
+
+    private void synthesizeStreaming(SynthesisRequest request) {
+        if (request.start(16000, AudioFormat.ENCODING_PCM_16BIT, 1) != TextToSpeech.SUCCESS) {
+            return;
+        }
+        byte[] data = { 0x01, 0x2 };
+        if (request.audioAvailable(data, 0, data.length) != TextToSpeech.SUCCESS) {
+            return;
+        }
+        if (request.done() != TextToSpeech.SUCCESS) {
+            return;
+        }
+    }
+
+    private void synthesizeComplete(SynthesisRequest request) {
+        byte[] data = { 0x01, 0x2 };
+        if (request.completeAudioAvailable(16000, AudioFormat.ENCODING_PCM_16BIT, 1,
+                data, 0, data.length) != TextToSpeech.SUCCESS) {
+            return;
+        }
+    }
+
+}
diff --git a/tests/src/android/webkit/cts/CtsTestServer.java b/tests/src/android/webkit/cts/CtsTestServer.java
index 91f4f1e..707455b 100644
--- a/tests/src/android/webkit/cts/CtsTestServer.java
+++ b/tests/src/android/webkit/cts/CtsTestServer.java
@@ -79,6 +79,7 @@
 
     public static final String FAVICON_PATH = "/favicon.ico";
     public static final String USERAGENT_PATH = "/useragent.html";
+    public static final String TEST_DOWNLOAD_PATH = "/download.html";
     public static final String ASSET_PREFIX = "/assets/";
     public static final String FAVICON_ASSET_PATH = ASSET_PREFIX + "webkit/favicon.png";
     public static final String APPCACHE_PATH = "/appcache.html";
@@ -350,6 +351,10 @@
         return sb.toString();
     }
 
+    public String getTestDownloadUrl() {
+        return getBaseUri() + TEST_DOWNLOAD_PATH;
+    }
+
     public String getLastRequestUrl() {
         return mLastQuery;
     }
@@ -501,6 +506,10 @@
             }
             response.setEntity(createEntity("<html><head><title>" + agent + "</title></head>" +
                     "<body>" + agent + "</body></html>"));
+        } else if (path.equals(TEST_DOWNLOAD_PATH)) {
+            response = createResponse(HttpStatus.SC_OK);
+            response.setHeader("Content-Length", "0");
+            response.setEntity(createEntity(""));
         } else if (path.equals(SHUTDOWN_PREFIX)) {
             response = createResponse(HttpStatus.SC_OK);
             // We cannot close the socket here, because we need to respond.
diff --git a/tests/src/android/webkit/cts/WebDriverStubActivity.java b/tests/src/android/webkit/cts/WebDriverStubActivity.java
new file mode 100644
index 0000000..092156b
--- /dev/null
+++ b/tests/src/android/webkit/cts/WebDriverStubActivity.java
@@ -0,0 +1,55 @@
+/*
+ * 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.app.Activity;
+import android.os.Bundle;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+import android.webkit.webdriver.WebDriver;
+
+/**
+ * A stub activity for WebDriver tests.
+ */
+public class WebDriverStubActivity extends Activity {
+    private WebDriver mDriver;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        WebView view = new WebView(this);
+
+        WebSettings settings = view.getSettings();
+        settings.setJavaScriptCanOpenWindowsAutomatically(true);
+        settings.setSupportMultipleWindows(true);
+        settings.setBuiltInZoomControls(true);
+        settings.setJavaScriptEnabled(true);
+        settings.setAppCacheEnabled(true);
+        settings.setDatabaseEnabled(true);
+        settings.setDomStorageEnabled(true);
+        settings.setGeolocationEnabled(true);
+        settings.setSaveFormData(true);
+
+        mDriver = new WebDriver(view);
+
+        setContentView(view);
+    }
+
+    public WebDriver getDriver() {
+        return mDriver;
+    }
+}
diff --git a/tests/tests/app/Android.mk b/tests/tests/app/Android.mk
index 46cd43b..a9b75df 100644
--- a/tests/tests/app/Android.mk
+++ b/tests/tests/app/Android.mk
@@ -22,7 +22,6 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := CtsTestAnnotationsLib
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/app/src/android/app/cts/DownloadManagerTest.java b/tests/tests/app/src/android/app/cts/DownloadManagerTest.java
new file mode 100644
index 0000000..fbe57c4
--- /dev/null
+++ b/tests/tests/app/src/android/app/cts/DownloadManagerTest.java
@@ -0,0 +1,183 @@
+/*
+ * 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.app.cts;
+
+import android.app.DownloadManager;
+import android.app.DownloadManager.Query;
+import android.app.DownloadManager.Request;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.Cursor;
+import android.net.Uri;
+import android.test.AndroidTestCase;
+import android.view.animation.cts.DelayedCheck;
+import android.webkit.cts.CtsTestServer;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class DownloadManagerTest extends AndroidTestCase {
+
+    private DownloadManager mDownloadManager;
+
+    private CtsTestServer mWebServer;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mDownloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
+        mWebServer = new CtsTestServer(mContext);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        mWebServer.shutdown();
+    }
+
+    public void testDownloadManager() throws Exception {
+        DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
+        try {
+            removeAllDownloads();
+
+            IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
+            mContext.registerReceiver(receiver, intentFilter);
+
+            long goodId = mDownloadManager.enqueue(new Request(getGoodUrl()));
+            long badId = mDownloadManager.enqueue(new Request(getBadUrl()));
+
+            int allDownloads = getTotalNumberDownloads();
+            assertEquals(2, allDownloads);
+
+            assertDownloadQueryableById(goodId);
+            assertDownloadQueryableById(badId);
+
+            receiver.waitForDownloadComplete();
+
+            assertDownloadQueryableByStatus(DownloadManager.STATUS_SUCCESSFUL);
+            assertDownloadQueryableByStatus(DownloadManager.STATUS_FAILED);
+
+            assertRemoveDownload(goodId, allDownloads - 1);
+            assertRemoveDownload(badId, allDownloads - 2);
+        } finally {
+            mContext.unregisterReceiver(receiver);
+        }
+    }
+
+    private class DownloadCompleteReceiver extends BroadcastReceiver {
+
+        private final CountDownLatch mReceiveLatch = new CountDownLatch(2);
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            mReceiveLatch.countDown();
+        }
+
+        public void waitForDownloadComplete() throws InterruptedException {
+            assertTrue("Make sure you have WiFi or some other connectivity for this test.",
+                    mReceiveLatch.await(3, TimeUnit.SECONDS));
+        }
+    }
+
+    private void removeAllDownloads() {
+        if (getTotalNumberDownloads() > 0) {
+            Cursor cursor = null;
+            try {
+                Query query = new Query();
+                cursor = mDownloadManager.query(query);
+                int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_ID);
+                long[] removeIds = new long[cursor.getCount()];
+                for (int i = 0; cursor.moveToNext(); i++) {
+                    removeIds[i] = cursor.getLong(columnIndex);
+                }
+                assertEquals(removeIds.length, mDownloadManager.remove(removeIds));
+                assertEquals(0, getTotalNumberDownloads());
+            } finally {
+                if (cursor != null) {
+                    cursor.close();
+                }
+            }
+        }
+    }
+
+    private Uri getGoodUrl() {
+        return Uri.parse(mWebServer.getTestDownloadUrl());
+    }
+
+    private Uri getBadUrl() {
+        return Uri.parse(mWebServer.getBaseUri() + "/nosuchurl");
+    }
+
+    private int getTotalNumberDownloads() {
+        Cursor cursor = null;
+        try {
+            Query query = new Query();
+            cursor = mDownloadManager.query(query);
+            return cursor.getCount();
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+
+    private void assertDownloadQueryableById(long downloadId) {
+        Cursor cursor = null;
+        try {
+            Query query = new Query().setFilterById(downloadId);
+            cursor = mDownloadManager.query(query);
+            assertEquals(1, cursor.getCount());
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+
+    private void assertDownloadQueryableByStatus(final int status) {
+        new DelayedCheck() {
+            @Override
+            protected boolean check() {
+                Cursor cursor= null;
+                try {
+                    Query query = new Query().setFilterByStatus(status);
+                    cursor = mDownloadManager.query(query);
+                    return 1 == cursor.getCount();
+                } finally {
+                    if (cursor != null) {
+                        cursor.close();
+                    }
+                }
+            }
+        }.run();
+    }
+
+    private void assertRemoveDownload(long removeId, int expectedNumDownloads) {
+        Cursor cursor = null;
+        try {
+            assertEquals(1, mDownloadManager.remove(removeId));
+            Query query = new Query();
+            cursor = mDownloadManager.query(query);
+            assertEquals(expectedNumDownloads, cursor.getCount());
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+}
diff --git a/tests/tests/content/src/android/content/cts/ContentResolverSyncTestCase.java b/tests/tests/content/src/android/content/cts/ContentResolverSyncTestCase.java
index b8c5d2e..1cc4cfb 100644
--- a/tests/tests/content/src/android/content/cts/ContentResolverSyncTestCase.java
+++ b/tests/tests/content/src/android/content/cts/ContentResolverSyncTestCase.java
@@ -217,12 +217,13 @@
     /**
      * Test if we can set and get the MasterSyncAutomatically switch
      */
-    public void testGetAndSetMasterSyncAutomatically() {
+    public void testGetAndSetMasterSyncAutomatically() throws Exception {
         ContentResolver.setMasterSyncAutomatically(true);
         assertEquals(true, ContentResolver.getMasterSyncAutomatically());
 
         ContentResolver.setMasterSyncAutomatically(false);
         assertEquals(false, ContentResolver.getMasterSyncAutomatically());
+        Thread.sleep(3000);
     }
 
     /**
diff --git a/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java b/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java
new file mode 100644
index 0000000..b229d03
--- /dev/null
+++ b/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java
@@ -0,0 +1,257 @@
+/*
+ * 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.os.storage.cts;
+
+import com.android.cts.stub.R;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.os.cts.FileUtils;
+import android.os.storage.OnObbStateChangeListener;
+import android.os.storage.StorageManager;
+import android.test.AndroidTestCase;
+import android.test.ComparisonFailure;
+import android.util.Log;
+
+import java.io.File;
+import java.io.InputStream;
+
+public class StorageManagerTest extends AndroidTestCase {
+
+    private static final String TAG = StorageManager.class.getSimpleName();
+
+    private static final long MAX_WAIT_TIME = 25*1000;
+    private static final long WAIT_TIME_INCR = 5*1000;
+
+    private static final String OBB_MOUNT_PREFIX = "/mnt/obb/";
+
+    private StorageManager mStorageManager;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mStorageManager = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE);
+    }
+
+    public void testMountAndUnmountObbNormal() {
+        final File outFile = getFilePath("test1.obb");
+
+        mountObb(R.raw.test1, outFile, OnObbStateChangeListener.MOUNTED);
+
+        mountObb(R.raw.test1, outFile, OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
+
+        final String mountPath = checkMountedPath(outFile);
+        final File mountDir = new File(mountPath);
+
+        assertTrue("OBB mounted path should be a directory", mountDir.isDirectory());
+
+        unmountObb(outFile, OnObbStateChangeListener.UNMOUNTED);
+    }
+
+    public void testAttemptMountNonObb() {
+        final File outFile = getFilePath("test1_nosig.obb");
+
+        mountObb(R.raw.test1_nosig, outFile, OnObbStateChangeListener.ERROR_INTERNAL);
+
+        assertFalse("OBB should not be mounted",
+                mStorageManager.isObbMounted(outFile.getPath()));
+
+        assertNull("OBB's mounted path should be null",
+                mStorageManager.getMountedObbPath(outFile.getPath()));
+    }
+
+    public void testAttemptMountObbWrongPackage() {
+        final File outFile = getFilePath("test1_wrongpackage.obb");
+
+        mountObb(R.raw.test1_wrongpackage, outFile,
+                OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
+
+        assertFalse("OBB should not be mounted",
+                mStorageManager.isObbMounted(outFile.getPath()));
+
+        assertNull("OBB's mounted path should be null",
+                mStorageManager.getMountedObbPath(outFile.getPath()));
+    }
+
+    public void testMountAndUnmountTwoObbs() {
+        final File file1 = getFilePath("test1.obb");
+        final File file2 = getFilePath("test2.obb");
+
+        ObbObserver oo1 = mountObbWithoutWait(R.raw.test1, file1);
+        ObbObserver oo2 = mountObbWithoutWait(R.raw.test1, file2);
+
+        Log.d(TAG, "Waiting for OBB #1 to complete mount");
+        waitForObbActionCompletion(file1, oo1, OnObbStateChangeListener.MOUNTED, false);
+        Log.d(TAG, "Waiting for OBB #2 to complete mount");
+        waitForObbActionCompletion(file2, oo2, OnObbStateChangeListener.MOUNTED, false);
+
+        final String mountPath1 = checkMountedPath(file1);
+        final File mountDir1 = new File(mountPath1);
+        assertTrue("OBB mounted path should be a directory", mountDir1.isDirectory());
+
+        final String mountPath2 = checkMountedPath(file2);
+        final File mountDir2 = new File(mountPath2);
+        assertTrue("OBB mounted path should be a directory", mountDir2.isDirectory());
+
+        unmountObb(file1, OnObbStateChangeListener.UNMOUNTED);
+        unmountObb(file2, OnObbStateChangeListener.UNMOUNTED);
+    }
+
+    private static void assertStartsWith(String message, String prefix, String actual) {
+        if (!actual.startsWith(prefix)) {
+            throw new ComparisonFailure(message, prefix, actual);
+        }
+    }
+
+    private static class ObbObserver extends OnObbStateChangeListener {
+        private String path;
+
+        public int state = -1;
+        boolean done = false;
+
+        @Override
+        public void onObbStateChange(String path, int state) {
+            Log.d(TAG, "Received message.  path=" + path + ", state=" + state);
+            synchronized (this) {
+                this.path = path;
+                this.state = state;
+                done = true;
+                notifyAll();
+            }
+        }
+
+        public String getPath() {
+            assertTrue("Expected ObbObserver to have received a state change.", done);
+            return path;
+        }
+
+        public int getState() {
+            assertTrue("Expected ObbObserver to have received a state change.", done);
+            return state;
+        }
+
+        public boolean isDone() {
+            return done;
+        }
+
+        public boolean waitForCompletion() {
+            long waitTime = 0;
+            synchronized (this) {
+                while (!isDone() && waitTime < MAX_WAIT_TIME) {
+                    try {
+                        wait(WAIT_TIME_INCR);
+                        waitTime += WAIT_TIME_INCR;
+                    } catch (InterruptedException e) {
+                        Log.i(TAG, "Interrupted during sleep", e);
+                    }
+                }
+            }
+
+            return isDone();
+        }
+    }
+
+    private File getFilePath(String name) {
+        final File filesDir = mContext.getFilesDir();
+        final File outFile = new File(filesDir, name);
+        return outFile;
+    }
+
+    private void copyRawToFile(int rawResId, File outFile) {
+        Resources res = mContext.getResources();
+        InputStream is = null;
+        try {
+            is = res.openRawResource(rawResId);
+        } catch (NotFoundException e) {
+            fail("Failed to load resource with id: " + rawResId);
+        }
+        FileUtils.setPermissions(outFile.getPath(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
+                | FileUtils.S_IRWXO);
+        assertTrue(FileUtils.copyToFile(is, outFile));
+        FileUtils.setPermissions(outFile.getPath(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
+                | FileUtils.S_IRWXO);
+    }
+
+    private void mountObb(final int resource, final File file, int expectedState) {
+        copyRawToFile(resource, file);
+
+        final ObbObserver observer = new ObbObserver();
+        assertTrue("mountObb call on " + file.getPath() + " should succeed",
+                mStorageManager.mountObb(file.getPath(), null, observer));
+
+        assertTrue("Mount should have completed",
+                observer.waitForCompletion());
+
+        if (expectedState == OnObbStateChangeListener.MOUNTED) {
+            assertTrue("OBB should be mounted", mStorageManager.isObbMounted(file.getPath()));
+        }
+
+        assertEquals("Actual file and resolved file should be the same",
+                file.getPath(), observer.getPath());
+
+        assertEquals(expectedState, observer.getState());
+    }
+
+    private ObbObserver mountObbWithoutWait(final int resource, final File file) {
+        copyRawToFile(resource, file);
+
+        final ObbObserver observer = new ObbObserver();
+        assertTrue("mountObb call on " + file.getPath() + " should succeed",
+                mStorageManager.mountObb(file.getPath(), null, observer));
+
+        return observer;
+    }
+
+    private void waitForObbActionCompletion(final File file,
+            final ObbObserver observer, int expectedState, boolean checkPath) {
+        assertTrue("Mount should have completed", observer.waitForCompletion());
+
+        assertTrue("OBB should be mounted", mStorageManager.isObbMounted(file.getPath()));
+
+        if (checkPath) {
+            assertEquals("Actual file and resolved file should be the same", file.getPath(),
+                    observer.getPath());
+        }
+
+        assertEquals(expectedState, observer.getState());
+    }
+
+    private String checkMountedPath(final File file) {
+        final String mountPath = mStorageManager.getMountedObbPath(file.getPath());
+        assertStartsWith("Path should be in " + OBB_MOUNT_PREFIX,
+                OBB_MOUNT_PREFIX,
+                mountPath);
+        return mountPath;
+    }
+
+    private void unmountObb(final File file, int expectedState) {
+        final ObbObserver observer = new ObbObserver();
+
+        assertTrue("unmountObb call on test1.obb should succeed",
+                mStorageManager.unmountObb(file.getPath(), false, observer));
+
+        assertTrue("Unmount should have completed",
+                observer.waitForCompletion());
+
+        assertEquals(expectedState, observer.getState());
+
+        if (expectedState == OnObbStateChangeListener.UNMOUNTED) {
+            assertFalse("OBB should not be mounted", mStorageManager.isObbMounted(file.getPath()));
+        }
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/AllocationTest.java b/tests/tests/renderscript/src/android/renderscript/cts/AllocationTest.java
index b6afb70..aa981ca 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/AllocationTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/AllocationTest.java
@@ -216,7 +216,7 @@
         }
 
         try {
-            int invalidUsage = 0x0010;
+            int invalidUsage = 0x0020;
             Allocation.createFromBitmap(mRS, B,
                 Allocation.MipmapControl.MIPMAP_NONE, invalidUsage);
             fail("should throw RSIllegalArgumentException.");
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ComputeTest.java b/tests/tests/renderscript/src/android/renderscript/cts/ComputeTest.java
index 8d24227..d1254cc 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ComputeTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ComputeTest.java
@@ -22,6 +22,10 @@
 import android.renderscript.Byte3;
 import android.renderscript.Byte4;
 
+import android.renderscript.Double2;
+import android.renderscript.Double3;
+import android.renderscript.Double4;
+
 import android.renderscript.Float2;
 import android.renderscript.Float3;
 import android.renderscript.Float4;
@@ -46,15 +50,52 @@
         Byte2 b2 = new Byte2();
         b2.x = 1;
         b2.y = 2;
+        b2 = new Byte2((byte)1, (byte)2);
+        assertTrue(b2.x == 1);
+        assertTrue(b2.y == 2);
         Byte3 b3 = new Byte3();
         b3.x = 1;
         b3.y = 2;
         b3.z = 2;
+        b3 = new Byte3((byte)1, (byte)2, (byte)3);
+        assertTrue(b3.x == 1);
+        assertTrue(b3.y == 2);
+        assertTrue(b3.z == 3);
         Byte4 b4 = new Byte4();
         b4.x = 1;
         b4.y = 2;
         b4.x = 3;
         b4.w = 4;
+        b4 = new Byte4((byte)1, (byte)2, (byte)3, (byte)4);
+        assertTrue(b4.x == 1);
+        assertTrue(b4.y == 2);
+        assertTrue(b4.z == 3);
+        assertTrue(b4.w == 4);
+
+        Double2 d2 = new Double2();
+        d2.x = 1.0;
+        d2.y = 2.0;
+        d2 = new Double2(1.0, 2.0);
+        assertTrue(d2.x == 1.0);
+        assertTrue(d2.y == 2.0);
+        Double3 d3 = new Double3();
+        d3.x = 1.0;
+        d3.y = 2.0;
+        d3.z = 3.0;
+        d3 = new Double3(1.0, 2.0, 3.0);
+        assertTrue(d3.x == 1.0);
+        assertTrue(d3.y == 2.0);
+        assertTrue(d3.z == 3.0);
+        Double4 d4 = new Double4();
+        d4.x = 1.0;
+        d4.y = 2.0;
+        d4.x = 3.0;
+        d4.w = 4.0;
+        d4 = new Double4(1.0, 2.0, 3.0, 4.0);
+        assertTrue(d4.x == 1.0);
+        assertTrue(d4.y == 2.0);
+        assertTrue(d4.z == 3.0);
+        assertTrue(d4.w == 4.0);
 
         Float2 f2 = new Float2();
         f2.x = 1.0f;
@@ -84,41 +125,77 @@
         Int2 i2 = new Int2();
         i2.x = 1;
         i2.y = 2;
+        i2 = new Int2(1, 2);
+        assertTrue(i2.x == 1);
+        assertTrue(i2.y == 2);
         Int3 i3 = new Int3();
         i3.x = 1;
         i3.y = 2;
         i3.z = 3;
+        i3 = new Int3(1, 2, 3);
+        assertTrue(i3.x == 1);
+        assertTrue(i3.y == 2);
+        assertTrue(i3.z == 3);
         Int4 i4 = new Int4();
         i4.x = 1;
         i4.y = 2;
         i4.x = 3;
         i4.w = 4;
+        i4 = new Int4(1, 2, 3, 4);
+        assertTrue(i4.x == 1);
+        assertTrue(i4.y == 2);
+        assertTrue(i4.z == 3);
+        assertTrue(i4.w == 4);
 
         Long2 l2 = new Long2();
         l2.x = 1;
         l2.y = 2;
+        l2 = new Long2(1, 2);
+        assertTrue(l2.x == 1);
+        assertTrue(l2.y == 2);
         Long3 l3 = new Long3();
         l3.x = 1;
         l3.y = 2;
         l3.z = 3;
+        l3 = new Long3(1, 2, 3);
+        assertTrue(l3.x == 1);
+        assertTrue(l3.y == 2);
+        assertTrue(l3.z == 3);
         Long4 l4 = new Long4();
         l4.x = 1;
         l4.y = 2;
         l4.x = 3;
         l4.w = 4;
+        l4 = new Long4(1, 2, 3, 4);
+        assertTrue(l4.x == 1);
+        assertTrue(l4.y == 2);
+        assertTrue(l4.z == 3);
+        assertTrue(l4.w == 4);
 
         Short2 s2 = new Short2();
         s2.x = 1;
         s2.y = 2;
+        s2 = new Short2((short)1, (short)2);
+        assertTrue(s2.x == 1);
+        assertTrue(s2.y == 2);
         Short3 s3 = new Short3();
         s3.x = 1;
         s3.y = 2;
         s3.z = 3;
+        s3 = new Short3((short)1, (short)2, (short)3);
+        assertTrue(s3.x == 1);
+        assertTrue(s3.y == 2);
+        assertTrue(s3.z == 3);
         Short4 s4 = new Short4();
         s4.x = 1;
         s4.y = 2;
         s4.x = 3;
         s4.w = 4;
+        s4 = new Short4((short)1, (short)2, (short)3, (short)4);
+        assertTrue(s4.x == 1);
+        assertTrue(s4.y == 2);
+        assertTrue(s4.z == 3);
+        assertTrue(s4.w == 4);
     }
 
     private boolean initializeGlobals(ScriptC_primitives s) {
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ElementTest.java b/tests/tests/renderscript/src/android/renderscript/cts/ElementTest.java
index 7723c20..0f58d55 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ElementTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ElementTest.java
@@ -244,8 +244,9 @@
         assertEquals(DataKind.PIXEL_LA, DataKind.valueOf("PIXEL_LA"));
         assertEquals(DataKind.PIXEL_RGB, DataKind.valueOf("PIXEL_RGB"));
         assertEquals(DataKind.PIXEL_RGBA, DataKind.valueOf("PIXEL_RGBA"));
+        assertEquals(DataKind.PIXEL_DEPTH, DataKind.valueOf("PIXEL_DEPTH"));
         // Make sure no new enums are added
-        assertEquals(6, DataKind.values().length);
+        assertEquals(7, DataKind.values().length);
 
         for (DataKind dk : DataKind.values()) {
             if (dk != DataKind.USER) {
diff --git a/tests/tests/speech/Android.mk b/tests/tests/speech/Android.mk
index a15b2ec..932c564 100755
--- a/tests/tests/speech/Android.mk
+++ b/tests/tests/speech/Android.mk
@@ -29,5 +29,7 @@
 
 LOCAL_SDK_VERSION := current
 
+LOCAL_INSTRUMENTATION_FOR := CtsTestStubs
+
 include $(BUILD_PACKAGE)
 
diff --git a/tests/tests/speech/AndroidManifest.xml b/tests/tests/speech/AndroidManifest.xml
index 6e1e528..778f763 100755
--- a/tests/tests/speech/AndroidManifest.xml
+++ b/tests/tests/speech/AndroidManifest.xml
@@ -24,9 +24,8 @@
 
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
 
-    <!--  self-instrumenting test package. -->
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
-                     android:targetPackage="com.android.cts.speech"
+    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+                     android:targetPackage="com.android.cts.stub"
                      android:label="CTS tests of android.speech"/>
 
 </manifest>
diff --git a/tests/tests/speech/src/android/speech/tts/cts/TextToSpeechServiceTest.java b/tests/tests/speech/src/android/speech/tts/cts/TextToSpeechServiceTest.java
new file mode 100644
index 0000000..7babe5a
--- /dev/null
+++ b/tests/tests/speech/src/android/speech/tts/cts/TextToSpeechServiceTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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.speech.tts.cts;
+
+import android.os.Environment;
+import android.speech.tts.TextToSpeech;
+import android.test.AndroidTestCase;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.HashMap;
+
+/**
+ * Tests for {@link android.speech.tts.TextToSpeechService} using StubTextToSpeechService.
+ */
+public class TextToSpeechServiceTest extends AndroidTestCase {
+
+    private static final String UTTERANCE_ID = "utterance";
+    private static final String SAMPLE_FILE_NAME = "mytts.wav";
+
+    private TextToSpeechWrapper mTts;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mTts = TextToSpeechWrapper.createTextToSpeechMockWrapper(getContext());
+        assertNotNull(mTts);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        mTts.shutdown();
+    }
+
+    private TextToSpeech getTts() {
+        return mTts.getTts();
+    }
+
+    public void testSynthesizeToFileStreaming() throws Exception {
+        File sampleFile = new File(Environment.getExternalStorageDirectory(), SAMPLE_FILE_NAME);
+        try {
+            assertFalse(sampleFile.exists());
+
+            int result = getTts().synthesizeToFile(StubTextToSpeechService.TEXT_STREAM,
+                    createParams(), sampleFile.getPath());
+            assertEquals("synthesizeToFile() failed", TextToSpeech.SUCCESS, result);
+
+            assertTrue("synthesizeToFile() completion timeout", mTts.waitForComplete(UTTERANCE_ID));
+            assertTrue("synthesizeToFile() didn't produce a file", sampleFile.exists());
+            assertTrue("synthesizeToFile() produced a non-sound file",
+                    TextToSpeechWrapper.isSoundFile(sampleFile.getPath()));
+        } finally {
+            sampleFile.delete();
+        }
+    }
+
+    public void testSynthesizeToFileComplete() throws Exception {
+        File sampleFile = new File(Environment.getExternalStorageDirectory(), SAMPLE_FILE_NAME);
+        try {
+            assertFalse(sampleFile.exists());
+
+            int result = getTts().synthesizeToFile(StubTextToSpeechService.TEXT_COMPLETE,
+                    createParams(), sampleFile.getPath());
+            assertEquals("synthesizeToFile() failed", TextToSpeech.SUCCESS, result);
+
+            assertTrue("synthesizeToFile() completion timeout", waitForUtterance());
+            assertTrue("synthesizeToFile() didn't produce a file", sampleFile.exists());
+            assertTrue("synthesizeToFile() produced a non-sound file",
+                    TextToSpeechWrapper.isSoundFile(sampleFile.getPath()));
+        } finally {
+            sampleFile.delete();
+        }
+    }
+
+    public void testSpeakStreaming() throws Exception {
+        int result = getTts().speak(StubTextToSpeechService.TEXT_STREAM, TextToSpeech.QUEUE_FLUSH,
+                createParams());
+        assertEquals("speak() failed", TextToSpeech.SUCCESS, result);
+        assertTrue("speak() completion timeout", waitForUtterance());
+    }
+
+    public void testSpeakComplete() throws Exception {
+        int result = getTts().speak(StubTextToSpeechService.TEXT_COMPLETE, TextToSpeech.QUEUE_FLUSH,
+                createParams());
+        assertEquals("speak() failed", TextToSpeech.SUCCESS, result);
+        assertTrue("speak() completion timeout", waitForUtterance());
+    }
+
+    public void testMediaPlayerFails() throws Exception {
+        File sampleFile = new File(Environment.getExternalStorageDirectory(), "notsound.wav");
+        try {
+            assertFalse(TextToSpeechWrapper.isSoundFile(sampleFile.getPath()));
+            FileOutputStream out = new FileOutputStream(sampleFile);
+            out.write(new byte[] { 0x01, 0x02 });
+            out.close();
+            assertFalse(TextToSpeechWrapper.isSoundFile(sampleFile.getPath()));
+        } finally {
+            sampleFile.delete();
+        }
+    }
+
+    private HashMap<String, String> createParams() {
+        HashMap<String, String> params = new HashMap<String,String>();
+        params.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, UTTERANCE_ID);
+        return params;
+    }
+
+    private boolean waitForUtterance() throws InterruptedException {
+        return mTts.waitForComplete(UTTERANCE_ID);
+    }
+
+}
diff --git a/tests/tests/speech/src/android/speech/tts/cts/TextToSpeechTest.java b/tests/tests/speech/src/android/speech/tts/cts/TextToSpeechTest.java
index e012fe3..cb8aa81 100644
--- a/tests/tests/speech/src/android/speech/tts/cts/TextToSpeechTest.java
+++ b/tests/tests/speech/src/android/speech/tts/cts/TextToSpeechTest.java
@@ -15,98 +15,31 @@
  */
 package android.speech.tts.cts;
 
-import dalvik.annotation.TestTargetClass;
-
-import android.media.MediaPlayer;
 import android.os.Environment;
 import android.speech.tts.TextToSpeech;
-import android.speech.tts.TextToSpeech.OnInitListener;
-import android.speech.tts.TextToSpeech.OnUtteranceCompletedListener;
 import android.test.AndroidTestCase;
-import android.util.Log;
 
 import java.io.File;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Locale;
 
 /**
  * Tests for {@link android.speech.tts.TextToSpeech}
  */
-@TestTargetClass(TextToSpeech.class)
 public class TextToSpeechTest extends AndroidTestCase {
 
-    private TextToSpeech mTts;
-
-    private static final String UTTERANCE = "utterance";
+    private static final String UTTERANCE_ID = "utterance";
     private static final String SAMPLE_TEXT = "This is a sample text to speech string";
     private static final String SAMPLE_FILE_NAME = "mytts.wav";
-    /** maximum time to wait for tts to be initialized */
-    private static final int TTS_INIT_MAX_WAIT_TIME = 30 * 1000;
-    /** maximum time to wait for speech call to be complete */
-    private static final int TTS_SPEECH_MAX_WAIT_TIME = 5 * 1000;
-    private static final String LOG_TAG = "TextToSpeechTest";
 
-    /**
-     * Listener for waiting for TTS engine initialization completion.
-     */
-    private static class InitWaitListener implements OnInitListener {
-        private int mStatus = TextToSpeech.ERROR;
-
-        public void onInit(int status) {
-            mStatus = status;
-            synchronized(this) {
-                notify();
-            }
-        }
-
-        public boolean waitForInit() throws InterruptedException {
-            if (mStatus == TextToSpeech.SUCCESS) {
-                return true;
-            }
-            synchronized (this) {
-                wait(TTS_INIT_MAX_WAIT_TIME);
-            }
-            return mStatus == TextToSpeech.SUCCESS;
-        }
-    }
-
-    /**
-     * Listener for waiting for utterance completion.
-     */
-    private static class UtteranceWaitListener implements OnUtteranceCompletedListener {
-        private boolean mIsComplete = false;
-        private final String mExpectedUtterance;
-
-        public UtteranceWaitListener(String expectedUtteranceId) {
-            mExpectedUtterance = expectedUtteranceId;
-        }
-
-        public void onUtteranceCompleted(String utteranceId) {
-            if (mExpectedUtterance.equals(utteranceId)) {
-                synchronized(this) {
-                    mIsComplete = true;
-                    notify();
-                }
-            }
-        }
-
-        public boolean waitForComplete() throws InterruptedException {
-            if (mIsComplete) {
-                return true;
-            }
-            synchronized (this) {
-                wait(TTS_SPEECH_MAX_WAIT_TIME);
-                return mIsComplete;
-            }
-        }
-    }
+    private TextToSpeechWrapper mTts;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        InitWaitListener listener = new InitWaitListener();
-        mTts = new TextToSpeech(getContext(), listener);
-        assertTrue(listener.waitForInit());
+        mTts = TextToSpeechWrapper.createTextToSpeechWrapper(getContext());
+        assertNotNull(mTts);
         assertTrue(checkAndSetLanguageAvailable());
     }
 
@@ -116,76 +49,80 @@
         mTts.shutdown();
     }
 
+    private TextToSpeech getTts() {
+        return mTts.getTts();
+    }
+
     /**
      * Ensures at least one language is available for tts
      */
-    private boolean  checkAndSetLanguageAvailable() {
+    private boolean checkAndSetLanguageAvailable() {
         // checks if at least one language is available in Tts
         for (Locale locale : Locale.getAvailableLocales()) {
-            int availability = mTts.isLanguageAvailable(locale);
+            int availability = getTts().isLanguageAvailable(locale);
             if (availability == TextToSpeech.LANG_AVAILABLE ||
                 availability == TextToSpeech.LANG_COUNTRY_AVAILABLE ||
                 availability == TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE) {
-                mTts.setLanguage(locale);
+                getTts().setLanguage(locale);
                 return true;
             }
         }
         return false;
     }
 
-    /**
-     * Tests that {@link TextToSpeech#synthesizeToFile(String, java.util.HashMap, String)} produces
-     * a non-zero sized file.
-     * @throws InterruptedException
-     */
     public void testSynthesizeToFile() throws Exception {
         File sampleFile = new File(Environment.getExternalStorageDirectory(), SAMPLE_FILE_NAME);
         try {
             assertFalse(sampleFile.exists());
-            // use an utterance listener to determine when synthesizing is complete
-            HashMap<String, String> param = new HashMap<String,String>();
-            param.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, UTTERANCE);
-            UtteranceWaitListener listener = new UtteranceWaitListener(UTTERANCE);
-            mTts.setOnUtteranceCompletedListener(listener);
 
-            int result = mTts.synthesizeToFile(SAMPLE_TEXT, param, sampleFile.getPath());
-            assertEquals(TextToSpeech.SUCCESS, result);
+            int result = getTts().synthesizeToFile(SAMPLE_TEXT, createParams(),
+                    sampleFile.getPath());
+            assertEquals("synthesizeToFile() failed", TextToSpeech.SUCCESS, result);
 
-            assertTrue(listener.waitForComplete());
-            assertTrue(sampleFile.exists());
-            assertTrue(isMusicFile(sampleFile.getPath()));
-
+            assertTrue("synthesizeToFile() completion timeout", waitForUtterance());
+            assertTrue("synthesizeToFile() didn't produce a file", sampleFile.exists());
+            assertTrue("synthesizeToFile() produced a non-sound file",
+                    TextToSpeechWrapper.isSoundFile(sampleFile.getPath()));
         } finally {
-            deleteFile(sampleFile);
+            sampleFile.delete();
         }
     }
 
-    /**
-     * Determine if given file path is a valid, playable music file.
-     */
-    private boolean isMusicFile(String filePath) {
-        // use media player to play the file. If it succeeds with no exceptions, assume file is
-        //valid
-        try {
-            MediaPlayer mp = new MediaPlayer();
-            mp.setDataSource(filePath);
-            mp.prepare();
-            mp.start();
-            mp.stop();
-            return true;
-        } catch (Exception e) {
-            Log.e(LOG_TAG, "Exception while attempting to play music file", e);
-            return false;
-        }
+    public void testSpeak() throws Exception {
+        int result = getTts().speak(SAMPLE_TEXT, TextToSpeech.QUEUE_FLUSH, createParams());
+        assertEquals("speak() failed", TextToSpeech.SUCCESS, result);
+        assertTrue("speak() completion timeout", waitForUtterance());
     }
 
-    /**
-     * Deletes the file at given path
-     * @param sampleFilePath
-     */
-    private void deleteFile(File file) {
-        if (file != null && file.exists()) {
-            file.delete();
-        }
+    public void testGetEnginesIncludesDefault() throws Exception {
+        List<TextToSpeech.EngineInfo> engines = getTts().getEngines();
+        assertNotNull("getEngines() returned null", engines);
+        assertContainsEngine(getTts().getDefaultEngine(), engines);
     }
+
+    public void testGetEnginesIncludesMock() throws Exception {
+        List<TextToSpeech.EngineInfo> engines = getTts().getEngines();
+        assertNotNull("getEngines() returned null", engines);
+        assertContainsEngine(TextToSpeechWrapper.MOCK_TTS_ENGINE, engines);
+    }
+
+    private void assertContainsEngine(String engine, List<TextToSpeech.EngineInfo> engines) {
+        for (TextToSpeech.EngineInfo engineInfo : engines) {
+            if (engineInfo.name.equals(engine)) {
+                return;
+            }
+        }
+        fail("Engine " + engine + " not found");
+    }
+
+    private HashMap<String, String> createParams() {
+        HashMap<String, String> params = new HashMap<String,String>();
+        params.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, UTTERANCE_ID);
+        return params;
+    }
+
+    private boolean waitForUtterance() throws InterruptedException {
+        return mTts.waitForComplete(UTTERANCE_ID);
+    }
+
 }
diff --git a/tests/tests/speech/src/android/speech/tts/cts/TextToSpeechWrapper.java b/tests/tests/speech/src/android/speech/tts/cts/TextToSpeechWrapper.java
new file mode 100644
index 0000000..cba242f
--- /dev/null
+++ b/tests/tests/speech/src/android/speech/tts/cts/TextToSpeechWrapper.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2009 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.speech.tts.cts;
+
+import android.content.Context;
+import android.media.MediaPlayer;
+import android.speech.tts.TextToSpeech;
+import android.speech.tts.TextToSpeech.OnInitListener;
+import android.speech.tts.TextToSpeech.OnUtteranceCompletedListener;
+import android.util.Log;
+
+import java.util.HashSet;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Wrapper for {@link TextToSpeech} with some handy test functionality.
+ */
+public class TextToSpeechWrapper {
+    private static final String LOG_TAG = "TextToSpeechServiceTest";
+
+    public static final String MOCK_TTS_ENGINE = "com.android.cts.stub";
+
+    private final Context mContext;
+    private TextToSpeech mTts;
+    private final InitWaitListener mInitListener;
+    private final UtteranceWaitListener mUtteranceListener;
+
+    /** maximum time to wait for tts to be initialized */
+    private static final int TTS_INIT_MAX_WAIT_TIME = 30 * 1000;
+    /** maximum time to wait for speech call to be complete */
+    private static final int TTS_SPEECH_MAX_WAIT_TIME = 5 * 1000;
+
+    private TextToSpeechWrapper(Context context) {
+        mContext = context;
+        mInitListener = new InitWaitListener();
+        mUtteranceListener = new UtteranceWaitListener();
+    }
+
+    private boolean initTts() throws InterruptedException {
+        return initTts(new TextToSpeech(mContext, mInitListener));
+    }
+
+    private boolean initTts(String engine) throws InterruptedException {
+        return initTts(new TextToSpeech(mContext, mInitListener, engine));
+    }
+
+    private boolean initTts(TextToSpeech tts) throws InterruptedException {
+        mTts = tts;
+        if (!mInitListener.waitForInit()) {
+            return false;
+        }
+        mTts.setOnUtteranceCompletedListener(mUtteranceListener);
+        return true;
+    }
+
+    public boolean waitForComplete(String utteranceId) throws InterruptedException {
+        return mUtteranceListener.waitForComplete(utteranceId);
+    }
+
+    public TextToSpeech getTts() {
+        return mTts;
+    }
+
+    public void shutdown() {
+        mTts.shutdown();
+    }
+
+    public static TextToSpeechWrapper createTextToSpeechWrapper(Context context)
+            throws InterruptedException {
+        TextToSpeechWrapper wrapper = new TextToSpeechWrapper(context);
+        if (wrapper.initTts()) {
+            return wrapper;
+        } else {
+            return null;
+        }
+    }
+
+    public static TextToSpeechWrapper createTextToSpeechMockWrapper(Context context)
+            throws InterruptedException {
+        TextToSpeechWrapper wrapper = new TextToSpeechWrapper(context);
+        if (wrapper.initTts(MOCK_TTS_ENGINE)) {
+            return wrapper;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Listener for waiting for TTS engine initialization completion.
+     */
+    private static class InitWaitListener implements OnInitListener {
+        private final Lock mLock = new ReentrantLock();
+        private final Condition mDone  = mLock.newCondition();
+        private Integer mStatus = null;
+
+        public void onInit(int status) {
+            mLock.lock();
+            try {
+                mStatus = new Integer(status);
+                mDone.signal();
+            } finally {
+                mLock.unlock();
+            }
+        }
+
+        public boolean waitForInit() throws InterruptedException {
+            long timeOutNanos = TimeUnit.MILLISECONDS.toNanos(TTS_INIT_MAX_WAIT_TIME);
+            mLock.lock();
+            try {
+                while (mStatus == null) {
+                    if (timeOutNanos <= 0) {
+                        return false;
+                    }
+                    timeOutNanos = mDone.awaitNanos(timeOutNanos);
+                }
+                return mStatus == TextToSpeech.SUCCESS;
+            } finally {
+                mLock.unlock();
+            }
+        }
+    }
+
+    /**
+     * Listener for waiting for utterance completion.
+     */
+    private static class UtteranceWaitListener implements OnUtteranceCompletedListener {
+        private final Lock mLock = new ReentrantLock();
+        private final Condition mDone  = mLock.newCondition();
+        private final HashSet<String> mCompletedUtterances = new HashSet<String>();
+
+        public void onUtteranceCompleted(String utteranceId) {
+            mLock.lock();
+            try {
+                mCompletedUtterances.add(utteranceId);
+                mDone.signal();
+            } finally {
+                mLock.unlock();
+            }
+        }
+
+        public boolean waitForComplete(String utteranceId)
+                throws InterruptedException {
+            long timeOutNanos = TimeUnit.MILLISECONDS.toNanos(TTS_INIT_MAX_WAIT_TIME);
+            mLock.lock();
+            try {
+                while (!mCompletedUtterances.remove(utteranceId)) {
+                    if (timeOutNanos <= 0) {
+                        return false;
+                    }
+                    timeOutNanos = mDone.awaitNanos(timeOutNanos);
+                }
+                return true;
+            } finally {
+                mLock.unlock();
+            }
+        }
+    }
+
+    /**
+     * Determines if given file path is a valid, playable music file.
+     */
+    public static boolean isSoundFile(String filePath) {
+        // use media player to play the file. If it succeeds with no exceptions, assume file is
+        //valid
+        MediaPlayer mp = null;
+        try {
+            mp = new MediaPlayer();
+            mp.setDataSource(filePath);
+            mp.prepare();
+            mp.start();
+            mp.stop();
+            return true;
+        } catch (Exception e) {
+            Log.e(LOG_TAG, "Exception while attempting to play music file", e);
+            return false;
+        } finally {
+            if (mp != null) {
+                mp.release();
+            }
+        }
+    }
+
+}
diff --git a/tests/tests/telephony/src/android/telephony/cts/PhoneNumberUtilsTest.java b/tests/tests/telephony/src/android/telephony/cts/PhoneNumberUtilsTest.java
index 57e610b..23891e0 100644
--- a/tests/tests/telephony/src/android/telephony/cts/PhoneNumberUtilsTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/PhoneNumberUtilsTest.java
@@ -121,11 +121,11 @@
 
         // Test toCallerIDMinMatch
         assertNull(PhoneNumberUtils.toCallerIDMinMatch(null));
-        assertEquals("1414555", PhoneNumberUtils.toCallerIDMinMatch("17005554141"));
-        assertEquals("1414555", PhoneNumberUtils.toCallerIDMinMatch("1-700-555-4141"));
-        assertEquals("1414555", PhoneNumberUtils.toCallerIDMinMatch("1-700-555-4141,1234"));
-        assertEquals("1414555", PhoneNumberUtils.toCallerIDMinMatch("1-700-555-4141;1234"));
-        assertEquals("NN14555", PhoneNumberUtils.toCallerIDMinMatch("1-700-555-41NN"));
+//        assertEquals("1414555", PhoneNumberUtils.toCallerIDMinMatch("17005554141"));
+//        assertEquals("1414555", PhoneNumberUtils.toCallerIDMinMatch("1-700-555-4141"));
+//        assertEquals("1414555", PhoneNumberUtils.toCallerIDMinMatch("1-700-555-4141,1234"));
+//        assertEquals("1414555", PhoneNumberUtils.toCallerIDMinMatch("1-700-555-4141;1234"));
+//        assertEquals("NN14555", PhoneNumberUtils.toCallerIDMinMatch("1-700-555-41NN"));
         assertEquals("", PhoneNumberUtils.toCallerIDMinMatch(""));
         assertEquals("0032", PhoneNumberUtils.toCallerIDMinMatch("2300"));
         assertEquals("0032+", PhoneNumberUtils.toCallerIDMinMatch("+2300"));
diff --git a/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
index 80d44f7..d7233d5 100755
--- a/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
@@ -64,7 +64,8 @@
                     "45002",    // SKT Mobility
                     "45008",    // KT Mobility
                     "45006",    // LGT
-                    "311660"    // MetroPCS
+                    "311660",   // MetroPCS
+                    "310120"    // Sprint
             );
 
     // List of network operators that doesn't support Data(binary) SMS message
diff --git a/tests/tests/view/src/android/view/cts/ViewTest.java b/tests/tests/view/src/android/view/cts/ViewTest.java
index 6b5c0a1..c001871 100644
--- a/tests/tests/view/src/android/view/cts/ViewTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewTest.java
@@ -67,6 +67,7 @@
 import android.view.View.OnKeyListener;
 import android.view.View.OnLongClickListener;
 import android.view.View.OnTouchListener;
+import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
 import android.view.animation.cts.DelayedCheck;
@@ -4631,6 +4632,10 @@
             return DEFAULT_PARENT_STATE_SET;
         }
 
+        public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
+            return false;
+        }
+
         public static int[] getDefaultParentStateSet() {
             return DEFAULT_PARENT_STATE_SET;
         }
diff --git a/tests/tests/webkit/src/android/webkit/cts/TestHtmlConstants.java b/tests/tests/webkit/src/android/webkit/cts/TestHtmlConstants.java
index b2bdfc8..51f1201 100644
--- a/tests/tests/webkit/src/android/webkit/cts/TestHtmlConstants.java
+++ b/tests/tests/webkit/src/android/webkit/cts/TestHtmlConstants.java
@@ -54,6 +54,8 @@
     public static final String BLANK_PAGE_URL = "webkit/test_blankPage.html";
     public static final String ADD_JAVA_SCRIPT_INTERFACE_URL = "webkit/test_jsInterface.html";
 
+    public static final String FORM_PAGE_URL = "webkit/form_page.html";
+
     public static final String EXT_WEB_URL1 = "http://www.example.com/";
 
     public static final String getFileUrl(String assetName) {
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebDriverTest.java b/tests/tests/webkit/src/android/webkit/cts/WebDriverTest.java
new file mode 100644
index 0000000..e933420
--- /dev/null
+++ b/tests/tests/webkit/src/android/webkit/cts/WebDriverTest.java
@@ -0,0 +1,846 @@
+/*
+ * 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.test.ActivityInstrumentationTestCase2;
+import android.webkit.webdriver.By;
+import android.webkit.webdriver.WebDriver;
+import android.webkit.webdriver.WebDriverException;
+import android.webkit.webdriver.WebElement;
+import android.webkit.webdriver.WebElementNotFoundException;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static android.webkit.cts.TestHtmlConstants.FORM_PAGE_URL;
+import static android.webkit.cts.TestHtmlConstants.HELLO_WORLD_URL;
+
+/**
+ * Tests for {@link android.webkit.webdriver.WebDriver}.
+ */
+public class WebDriverTest extends
+        ActivityInstrumentationTestCase2<WebDriverStubActivity>{
+    private WebDriver mDriver;
+    private CtsTestServer mWebServer;
+    private static final String SOME_TEXT = "Some text";
+    private static final String DIV_TEXT =
+            "A div Nested text a nested link Foo Nested text";
+    private static final String NESTED_TEXT = "Nested text";
+    private static final String DIV_ID = "divId";
+    private static final String SOME_TEXT_ID = "someTextId";
+    private static final String BAD_ID = "BadId";
+    private static final String NESTED_LINK_ID = "nestedLinkId";
+    private static final String FIRST_DIV = "firstDiv";
+    private static final String INEXISTENT = "inexistent";
+    private static final String ID = "id";
+    private static final String OUTTER = "outter";
+
+    public WebDriverTest() {
+        super(WebDriverStubActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mDriver = getActivity().getDriver();
+        mWebServer = new CtsTestServer(getActivity(), false);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mWebServer.shutdown();
+        super.tearDown();
+    }
+
+    public void testGetIsBlocking() {
+        mDriver.get(mWebServer.getDelayedAssetUrl(HELLO_WORLD_URL));
+        assertTrue(mDriver.getPageSource().contains("hello world!"));
+    }
+
+    // getText
+    public void testGetTextReturnsEmptyString() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement emptyLink = mDriver.findElement(By.id("emptyLink"));
+        assertEquals("", emptyLink.getText());
+    }
+
+    // getAttribute
+    public void testGetValidAttribute() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement link = mDriver.findElement(By.linkText("Link=equalssign"));
+        assertEquals("foo", link.getAttribute("href"));
+    }
+
+    public void testGetInvalidAttributeReturnsNull() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement link = mDriver.findElement(By.linkText("Link=equalssign"));
+        assertNull(link.getAttribute(INEXISTENT));
+    }
+
+    public void testGetAttributeNotSetReturnsNull() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement link = mDriver.findElement(By.linkText("Link=equalssign"));
+        assertNull(link.getAttribute("disabled"));
+    }
+
+    // getTagName
+    public void testTagName() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement span = mDriver.findElement(By.tagName("span"));
+        assertEquals("SPAN", span.getTagName());
+    }
+
+    // isEnabled
+    public void testIsEnabled() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement div = mDriver.findElement(By.id(DIV_ID));
+        assertTrue(div.isEnabled());
+
+        WebElement input = mDriver.findElement(By.name("inputDisabled"));
+        assertFalse(input.isEnabled());
+    }
+
+    // isSelected
+    public void testIsSelected() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement optionOne = mDriver.findElement(By.id("one"));
+        assertTrue(optionOne.isSelected());
+
+        WebElement optionTwo = mDriver.findElement(By.id("two"));
+        assertFalse(optionTwo.isSelected());
+
+        WebElement selectEggs = mDriver.findElement(By.id("eggs"));
+        assertTrue(selectEggs.isSelected());
+
+        WebElement selectHam = mDriver.findElement(By.id("ham"));
+        assertFalse(selectHam.isSelected());
+
+        WebElement inputCheese = mDriver.findElement(By.id("cheese"));
+        assertFalse(inputCheese.isSelected());
+
+        WebElement inputCheesePeas = mDriver.findElement(
+                By.id("cheese_and_peas"));
+        assertTrue(inputCheesePeas.isSelected());
+    }
+
+    public void testIsSelectedOnHiddenInputThrows() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement inputHidden = mDriver.findElement(By.name("hidden"));
+        try {
+            inputHidden.isSelected();
+            fail();
+        } catch (WebDriverException e) {
+            // This is expcted
+        }
+    }
+
+    public void testIsSelectedOnNonSelectableElementThrows() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement link= mDriver.findElement(By.linkText("Foo"));
+        try {
+            link.isSelected();
+            fail();
+        } catch (WebDriverException e) {
+            // This is expected
+        }
+    }
+
+    // toogle
+    public void testToggleCheckbox() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement check = mDriver.findElement(By.id("checky"));
+        assertFalse(check.isSelected());
+        assertTrue(check.toggle());
+        assertFalse(check.toggle());
+    }
+
+    public void testToggleOnNonTogglableElements() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement inputHidden = mDriver.findElement(By.name("hidden"));
+        try {
+            inputHidden.toggle();
+            fail();
+        } catch (WebDriverException e) {
+            // This is expected
+        }
+    }
+
+    // findElement
+    public void testFindElementThrowsIfNoPageIsLoaded() {
+        try {
+            mDriver.findElement(By.id(SOME_TEXT_ID));
+            fail();
+        } catch (NullPointerException e) {
+            // this is expected
+        }
+    }
+
+    // findElements
+    public void testFindElementsThrowsIfNoPageIsLoaded() {
+        try {
+            mDriver.findElements(By.id(SOME_TEXT_ID));
+            fail();
+        } catch (NullPointerException e) {
+            // this is expected
+        }
+    }
+
+    // By id
+    public void testFindElementById() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement element = mDriver.findElement(By.id(SOME_TEXT_ID));
+        assertTrue(SOME_TEXT.equals(element.getText()));
+
+        element = mDriver.findElement(By.id(DIV_ID));
+        assertTrue(DIV_TEXT.equals(element.getText()));
+    }
+
+    public void testFindElementByIdThrowsIfElementDoesNotExists() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        try {
+            mDriver.findElement(By.id(BAD_ID));
+            fail("This should have failed.");
+        } catch (WebElementNotFoundException e) {
+            // This is expected
+        }
+    }
+
+    public void testFindNestedElementById() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement parent = mDriver.findElement(By.id(DIV_ID));
+        WebElement nestedNode = parent.findElement(By.id(NESTED_LINK_ID));
+        assertTrue(NESTED_TEXT.equals(nestedNode.getText()));
+    }
+
+    public void testFindElementsById() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        List<WebElement> elements = mDriver.findElements(By.id(ID + "3"));
+        assertEquals(2, elements.size());
+        assertEquals("A paragraph", elements.get(1).getText());
+    }
+
+    public void testFindElementsByIdReturnsEmptyListIfNoResultsFound() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        List<WebElement> elements = mDriver.findElements(By.id(INEXISTENT));
+        assertNotNull(elements);
+        assertEquals(0, elements.size());
+    }
+
+    public void testFindNestedElementsById() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement div = mDriver.findElement(By.name(FIRST_DIV));
+        List<WebElement> elements = div.findElements(By.id("n1"));
+        assertEquals(2, elements.size());
+        assertEquals("spann1", elements.get(1).getAttribute("name"));
+    }
+
+    // By linkText
+    public void testFindElementByLinkText() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement element = mDriver.findElement(By.linkText("Nested text"));
+        assertTrue(NESTED_LINK_ID.equals(element.getAttribute(ID)));
+    }
+
+    public void testFindElementByLinkTextThrowsIfElementDoesNotExists() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        try {
+            mDriver.findElement(By.linkText(INEXISTENT));
+            fail("This should have failed.");
+        } catch (WebElementNotFoundException e) {
+            // This is expected
+        }
+    }
+
+    public void testFindNestedElementByLinkText() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement parent = mDriver.findElement(By.id(DIV_ID));
+        WebElement nestedNode = parent.findElement(By.linkText("Foo"));
+        assertTrue("inner".equals(nestedNode.getAttribute(ID)));
+    }
+
+    public void testFindElementsByLinkText() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        List<WebElement> elements = mDriver.findElements(By.linkText("Foo"));
+        assertEquals(4, elements.size());
+    }
+
+    public void testFindElementsByLinkTextReturnsEmptyListIfNoResultsFound() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        List<WebElement> elements = mDriver.findElements(By.linkText("Boo"));
+        assertEquals(0, elements.size());
+    }
+
+    public void testFindNestedElementsByLinkText() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement div = mDriver.findElement(By.name(FIRST_DIV));
+        List<WebElement> elements =
+                div.findElements(By.linkText("Nested text"));
+        assertEquals(2, elements.size());
+    }
+
+    // By partialLinkText
+    public void testFindElementByPartialLinkText() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement element = mDriver.findElement(By.partialLinkText("text"));
+        assertTrue(SOME_TEXT.equals(element.getText()));
+    }
+
+    public void testFindElementByPartialLinkTextThrowsIfElementDoesNotExists() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        try {
+            mDriver.findElement(By.partialLinkText(INEXISTENT));
+            fail("This should have failed.");
+        } catch (WebElementNotFoundException e) {
+            // This is expected
+        }
+    }
+
+    public void testFindNestedElementByPartialLinkText() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement parent = mDriver.findElement(By.id(DIV_ID));
+        WebElement nestedNode = parent.findElement(By.partialLinkText("text"));
+        assertTrue(NESTED_TEXT.equals(nestedNode.getText()));
+    }
+
+    public void testFindElementsByPartialLinkText() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        List<WebElement> elements =
+                mDriver.findElements(By.partialLinkText("text"));
+        assertTrue(elements.size() > 2);
+    }
+
+    public void
+    testFindElementsByPartialLinkTextReturnsEmptyListIfNoResultsFound() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        List<WebElement> elements =
+                mDriver.findElements(By.partialLinkText(INEXISTENT));
+        assertEquals(0, elements.size());
+    }
+
+    public void testFindNestedElementsByPartialLinkText() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement div = mDriver.findElements(By.name(FIRST_DIV)).get(0);
+        List<WebElement> elements =
+                div.findElements(By.partialLinkText("text"));
+        assertEquals(2, elements.size());
+    }
+
+    // by name
+    public void testFindElementByName() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement element = mDriver.findElement(By.name("foo"));
+        assertTrue(OUTTER.equals(element.getAttribute(ID)));
+    }
+
+    public void testFindElementByNameThrowsIfElementDoesNotExists() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        try {
+            mDriver.findElement(By.name(INEXISTENT));
+            fail("This should have failed.");
+        } catch (WebElementNotFoundException e) {
+            // This is expected
+        }
+    }
+
+    public void testFindNestedElementByName() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement parent = mDriver.findElement(By.id(DIV_ID));
+        WebElement nestedNode = parent.findElement(By.name("nestedLink"));
+        assertTrue(NESTED_TEXT.equals(nestedNode.getText()));
+    }
+
+    public void testFindElementsByName() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        List<WebElement> elements = mDriver.findElements(By.name("text"));
+        assertEquals(2, elements.size());
+    }
+
+    public void testFindElementsByNameReturnsEmptyListIfNoResultsFound() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        List<WebElement> elements = mDriver.findElements(By.name(INEXISTENT));
+        assertEquals(0, elements.size());
+    }
+
+    public void testFindNestedElementsByName() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement div = mDriver.findElements(By.xpath(
+                "//div[@" + ID + "='divId']"))
+                .get(0);
+        List<WebElement> elements = div.findElements(By.name("foo"));
+        assertEquals(1, elements.size());
+    }
+
+    // By tagName
+    public void testFindElementByTagName() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement element = mDriver.findElement(By.tagName("a"));
+        assertTrue("Tag A".equals(element.getText()));
+    }
+
+    public void testFindElementByTagNameThrowsIfElementDoesNotExists() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        try {
+            mDriver.findElement(By.tagName(INEXISTENT));
+            fail("This should have failed.");
+        } catch (WebElementNotFoundException e) {
+            // This is expected
+        }
+    }
+
+    public void testFindNestedElementByTagName() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement parent = mDriver.findElement(By.id(DIV_ID));
+        WebElement nestedNode = parent.findElement(By.tagName("a"));
+        assertTrue(NESTED_TEXT.equals(nestedNode.getText()));
+    }
+
+    public void testFindElementsByTagName() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        List<WebElement> elements = mDriver.findElements(By.tagName("a"));
+        assertTrue(elements.size() > 0);
+    }
+
+    public void testFindElementsByTagNameReturnsEmptyListIfNoResultsFound() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        List<WebElement> elements = mDriver.findElements(
+                By.tagName(INEXISTENT));
+        assertEquals(0, elements.size());
+    }
+
+    public void testFindNestedElementsByTagName() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement div = mDriver.findElement(By.xpath(
+                "//div[@" + ID + "='divId']"));
+        List<WebElement> elements = div.findElements(By.tagName("span"));
+        assertEquals(1, elements.size());
+    }
+
+    // By xpath
+    public void testFindElementByXPath() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement element =
+                mDriver.findElement(By.xpath(
+                "//a[@" + ID + "=\"someTextId\"]"));
+        assertTrue(SOME_TEXT.equals(element.getText()));
+
+        element = mDriver.findElement(By.xpath("//div[@name='firstDiv']"));
+        assertTrue(DIV_TEXT.equals(element.getText()));
+    }
+
+    public void testFindElementByXPathThrowsIfElementDoesNotExists() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        try {
+            mDriver.findElement(By.xpath("//a[@" + ID + "='inexistant']"));
+            fail("This should have failed.");
+        } catch (WebElementNotFoundException e) {
+            // This is expected
+        }
+    }
+
+    public void testFindNestedElementByXPath() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement parent = mDriver.findElement(By.xpath(
+                "//div[@" + ID + "='divId']"));
+        WebElement nestedNode = parent.findElement(
+                By.xpath(".//a[@" + ID + "='nestedLinkId']"));
+        assertTrue(NESTED_TEXT.equals(nestedNode.getText()));
+    }
+
+    public void testFindElementsByXPath() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        List<WebElement> elements = mDriver.findElements(
+                By.xpath("//a[@name='foo']"));
+        assertTrue(elements.size() > 1);
+    }
+
+    public void testFindElementsByXPathReturnsEmptyListIfNoResultsFound() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        List<WebElement> elements =
+                mDriver.findElements(By.xpath(
+                        "//a[@" + ID + "='inexistant']"));
+        assertEquals(0, elements.size());
+    }
+
+    public void testFindNestedElementsByXPath() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement div = mDriver.findElements(By.xpath(
+                "//div[@" + ID + "='divId']"))
+                .get(0);
+        List<WebElement> elements = div.findElements(
+                By.xpath(".//a[@name='foo']"));
+        assertEquals(1, elements.size());
+    }
+
+    public void testFindElementByXpathWithInvalidXPath() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        try {
+            mDriver.findElement(By.xpath("//a@" + ID + "=inexistant']"));
+            fail("This should have failed.");
+        } catch (WebElementNotFoundException e) {
+            // This is expected
+        }
+    }
+
+    // By className
+    public void testFindElementByClassName() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement element = mDriver.findElement(By.className(" spaceAround "));
+        assertTrue("Spaced out".equals(element.getText()));
+    }
+
+    public void testFindElementByClassNameThrowsIfElementDoesNotExists() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        try {
+            mDriver.findElement(By.className("bou"));
+            fail("This should have failed.");
+        } catch (WebElementNotFoundException e) {
+            // This is expected
+        }
+    }
+
+    public void testFindNestedElementByClassName() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement parent = mDriver.findElement(By.id(DIV_ID));
+        WebElement nestedNode = parent.findElement(By.className("divClass"));
+        assertTrue(NESTED_TEXT.equals(nestedNode.getText()));
+    }
+
+    public void testFindElementsByClassName() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        List<WebElement> elements =
+                mDriver.findElements(By.className("divClass"));
+        assertTrue(elements.size() > 1);
+    }
+
+    public void testFindElementsByClassNameReturnsEmptyListIfNoResultsFound() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        List<WebElement> elements =
+                mDriver.findElements(By.className(INEXISTENT));
+        assertEquals(0, elements.size());
+    }
+
+    public void testFindNestedElementsByClassName() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement parent = mDriver.findElement(By.id(DIV_ID));
+        List<WebElement> nested =
+                parent.findElements(By.className("divClass"));
+        assertTrue(nested.size() > 0);
+    }
+
+    // By css
+    public void testFindElementByCss() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement element = mDriver.findElement(By.css("#" + "outter"));
+        assertTrue("Foo".equals(element.getText()));
+    }
+
+    public void testFindElementByCssThrowsIfElementDoesNotExists() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        try {
+            mDriver.findElement(By.css("bou.foo"));
+            fail("This should have failed.");
+        } catch (WebElementNotFoundException e) {
+            // This is expected
+        }
+    }
+
+    public void testFindNestedElementByCss() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement parent = mDriver.findElement(By.id(DIV_ID));
+        WebElement nestedNode = parent.findElement(
+                By.css("#" + NESTED_LINK_ID));
+        assertTrue(NESTED_TEXT.equals(nestedNode.getText()));
+    }
+
+    public void testFindElementsByCss() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        List<WebElement> elements = mDriver.findElements(
+                By.css("#" + SOME_TEXT_ID));
+        assertTrue(elements.size() > 0);
+    }
+
+    public void testFindElementsByCssReturnsEmptyListIfNoResultsFound() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        List<WebElement> elements = mDriver.findElements(By.css("bou.foo"));
+        assertEquals(0, elements.size());
+    }
+
+    public void testFindNestedElementsByCss() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement parent = mDriver.findElement(By.id(DIV_ID));
+        List<WebElement> nested = parent.findElements(
+                By.css("#" + NESTED_LINK_ID));
+        assertEquals(1, nested.size());
+    }
+
+    public void testExecuteScriptShouldReturnAString() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        Object result = mDriver.executeScript("return document.title");
+        assertEquals("Test Page", (String) result);
+    }
+
+    public void testExecuteScriptShouldReturnAWebElement() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        Object result = mDriver.executeScript(
+                "return document.getElementsByTagName('div')[0];");
+        assertTrue(result instanceof WebElement);
+        assertEquals(DIV_TEXT, ((WebElement) result).getText());
+    }
+
+    public void testExecuteScriptShouldPassAndReturnADouble() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        Double expected = new Double(1.2);
+        Object result = mDriver.executeScript("return arguments[0];", expected);
+        assertTrue(result instanceof Double);
+        assertEquals(expected, (Double) result);
+    }
+
+    public void testExecuteScriptShouldPassAndReturnALong() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        Long expected = new Long(1);
+        Object result = mDriver.executeScript("return arguments[0];", expected);
+        assertTrue(result instanceof Long);
+        assertEquals(expected, (Long) result);
+    }
+
+    public void testExecuteScriptShouldPassReturnABoolean() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        Boolean expected = new Boolean(true);
+        Object result = mDriver.executeScript("return arguments[0] === true;",
+                expected);
+        assertTrue(result instanceof Boolean);
+        assertEquals(expected, (Boolean) result);
+    }
+
+    public void testExecuteScriptShouldReturnAList() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        List<String> expected = new ArrayList();
+        expected.add("one");
+        expected.add("two");
+        expected.add("three");
+
+        Object result = mDriver.executeScript(
+                "return ['one', 'two', 'three'];");
+
+        assertTrue(expected.equals((List<String>) result));
+    }
+
+    public void testExecuteScriptShouldReturnNestedList() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        List<Object> expected = new ArrayList();
+        expected.add("one");
+        List<Object> nestedList = new ArrayList();
+        nestedList.add(true);
+        nestedList.add(false);
+        expected.add(nestedList);
+        Map<String, Object> nestedMap = new HashMap();
+        nestedMap.put("bread", "cheese");
+        nestedMap.put("hungry", true);
+        expected.add(nestedMap);
+
+        Object result = mDriver.executeScript(
+                "return ['one', [true, false], "
+                + "{bread:'cheese', hungry:true}];");
+
+        assertTrue(expected.equals(result));
+    }
+
+    public void testExecuteScriptShouldBeAbleToReturnALisOfwebElements() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        List<WebElement> result = (List<WebElement>) mDriver.executeScript(
+                "return document.getElementsByTagName('a')");
+        assertTrue(result.size() > 1);
+    }
+
+    public void testExecuteScriptShouldReturnAMap() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        Map<String, Object> expected = new HashMap();
+        expected.put("abc", Long.valueOf(123));
+        expected.put("cat", false);
+        Map<String, Object> nestedMap = new HashMap();
+        nestedMap.put("bread", "cheese");
+        nestedMap.put("hungry", true);
+        expected.put("map", nestedMap);
+        List<String> nestedList = new ArrayList();
+        nestedList.add("bou");
+        nestedList.add("truc");
+        expected.put("list", nestedList);
+
+        Object res = mDriver.executeScript("return {abc:123, cat:false, "
+                + "map:{bread:'cheese', hungry:true}, list:['bou', 'truc']};");
+        assertTrue(res instanceof Map);
+        Map<String, Object> result = (Map<String, Object>) res;
+        assertEquals(expected.size(), result.size());
+
+        assertTrue(expected.equals(result));
+    }
+
+    public void testExecuteScriptShouldThrowIfJsIsBad() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        try {
+            mDriver.executeScript("return bou();");
+            fail("This should have failed");
+        } catch (RuntimeException e) {
+            // This is expected.
+        }
+    }
+
+    public void testExecuteScriptShouldbeAbleToPassAString() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        String expected = "bou";
+        Object result = mDriver.executeScript("return arguments[0]", expected);
+        assertEquals(expected, (String) result);
+    }
+
+    public void testExecuteScriptShouldBeAbleToPassWebElement() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        WebElement div = mDriver.findElement(By.id(DIV_ID));
+        Object result = mDriver.executeScript(
+                "arguments[0]['flibble'] = arguments[0].getAttribute('"
+                + ID + "');"
+                + "return arguments[0]['flibble'];", div);
+        assertEquals(DIV_ID, (String) result);
+    }
+
+    public void testExecuteScriptShouldBeAbleToPassAList() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        List<String> expected = new ArrayList();
+        expected.add("apple");
+        expected.add("cheese");
+        expected.add("food");
+
+        Object result = mDriver.executeScript(
+                "return arguments[0].length", expected);
+        assertEquals(expected.size(), ((Long) result).intValue());
+    }
+
+    public void testExecuteScriptShouldBeAbleToPassNestedLists() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        List<Object> expected = new ArrayList();
+        expected.add("apple");
+        expected.add("cheese");
+        List<Integer> nested = new ArrayList();
+        nested.add(1);
+        nested.add(2);
+        expected.add(nested);
+        expected.add("food");
+
+        Object result = mDriver.executeScript(
+                "return arguments[0][2].length", expected);
+        assertEquals(nested.size(), ((Long) result).intValue());
+    }
+
+    public void testExecuteScriptShouldBeAbleToPassAMap() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        Map<String, String> expected = new HashMap();
+        expected.put("apple", "pie");
+        expected.put("cheese", "cake");
+
+        Object result = mDriver.executeScript(
+                "return arguments[0].apple", expected);
+        assertEquals(expected.get("apple"), (String) result);
+    }
+
+    public void testExecuteScriptShouldBeAbleToPassNestedMaps() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        Map<String, Object> expected = new HashMap();
+        expected.put("apple", "pie");
+        Map<String, String> nested = new HashMap();
+        nested.put("foo", "boo");
+        expected.put("nested", nested);
+        expected.put("cheese", "cake");
+
+        Object result = mDriver.executeScript(
+                "return arguments[0].nested.foo", expected);
+        assertEquals(((Map<String, Object>)expected.get("nested")).get("foo"),
+                (String) result);
+    }
+
+
+    public void testExecuteScriptShouldThrowIfArgumentIsNotValid() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        try {
+            mDriver.executeScript("return arguments[0];", mDriver);
+            fail("This should have failed");
+        } catch (RuntimeException e) {
+            // This is expected.
+        }
+    }
+
+    public void testExecuteScriptHandlesStringCorrectly() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        String cheese = "cheese";
+        String bread = "bread";
+        Object result = mDriver.executeScript(
+                "return 'I love ' + arguments[0] + ' and ' + arguments[1]",
+                cheese, bread);
+        assertEquals("I love cheese and bread", (String) result);
+    }
+
+    public void testExecuteScriptShouldThrowIfNoPageLoaded() {
+        try {
+            mDriver.executeScript("return 'bou';");
+            fail("This should have failed");
+        } catch (RuntimeException e) {
+            // This is expected.
+        }
+    }
+
+    public void testExecuteScriptShouldBeAbleToCreatePersistentValue() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        mDriver.executeScript("document.b" + ID + "ule = ['hello']");
+        Object result = mDriver.executeScript(
+                "return document.b" + ID + "ule.shift();");
+        assertEquals("hello", (String) result);
+    }
+
+    public void testExecuteScriptEscapesQuotesAndBackslash() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        assertTrue((Boolean) mDriver.executeScript(
+                "return \"foo'\\\"\" == arguments[0];", "foo'\""));
+        assertTrue((Boolean) mDriver.executeScript(
+                "return \"foo'\\\"bar\" == arguments[0];", "foo'\"bar"));
+        assertTrue((Boolean) mDriver.executeScript(
+                "return 'foo\"' == arguments[0];", "foo\""));
+        assertTrue((Boolean) mDriver.executeScript(
+                "return \"foo'\" == arguments[0];", "foo'"));
+        assertTrue((Boolean) mDriver.executeScript(
+                "return \"foo\\\\\\\"\" == arguments[0];", "foo\\\""));
+        assertTrue((Boolean) mDriver.executeScript(
+                "return \"f\\\"o\\\\o\\\\\\\\\\\"\" == arguments[0];",
+                "f\"o\\o\\\\\""));
+    }
+
+    public void testExecuteScriptReturnsNull() {
+        mDriver.get(mWebServer.getAssetUrl(FORM_PAGE_URL));
+        Object result = mDriver.executeScript("return null;");
+        assertNull(result);
+        result = mDriver.executeScript("return undefined;");
+        assertNull(result);
+    }
+
+    public void testExecuteScriptShouldThrowIfNoPageIsLoaded() {
+        try {
+            Object result = mDriver.executeScript("return null;");
+            fail();
+        } catch (Exception e) {
+
+        }
+    }
+}
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
index c28e162..c526a1f 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
@@ -664,12 +664,14 @@
         String url = TestHtmlConstants.EMBEDDED_IMG_URL;
         String ext = MimeTypeMap.getFileExtensionFromUrl(url);
         loadAssetUrl(url);
+        Thread.sleep(1000);
         assertFalse(mWebServer.getLastRequestUrl().endsWith(ext));
 
         mWebView.clearCache(true);
         mSettings.setLoadsImagesAutomatically(false);
         assertFalse(mSettings.getLoadsImagesAutomatically());
         loadAssetUrl(url);
+        Thread.sleep(1000);
         assertTrue(mWebServer.getLastRequestUrl().endsWith(ext));
     }
 
diff --git a/tools/host/etc/cts b/tools/host/etc/cts
index bb1fa2a..ec8ff0b 100755
--- a/tools/host/etc/cts
+++ b/tools/host/etc/cts
@@ -20,7 +20,6 @@
 DDMS_LIB=ddmlib-prebuilt.jar
 JUNIT_LIB=junit.jar
 HOSTTEST_LIB=hosttestlib.jar
-CTS_TEST_ANNOTATIONS_HOST_LIB=CtsTestAnnotationsHostLib.jar
 
 # Checking if "adb" is known by the system
 PATH=.:${PATH}
@@ -64,7 +63,6 @@
 ${ANDROID_ROOT}/${JAR_DIR}/${DDMS_LIB}:\
 ${ANDROID_ROOT}/${JAR_DIR}/${JUNIT_LIB}:\
 ${ANDROID_ROOT}/${JAR_DIR}/${HOSTTEST_LIB}:\
-${ANDROID_ROOT}/${JAR_DIR}/${CTS_TEST_ANNOTATIONS_HOST_LIB}:\
 ${ANDROID_ROOT}/${JAR_DIR}/${CTS_LIB}
 # Add path to CTS JAR file in the CTS archive
 CTS_LIBS=${CTS_LIBS}:${CTS_DIR}/${CTS_LIB}
diff --git a/tools/host/src/Android.mk b/tools/host/src/Android.mk
index d9195eb..dabee3d 100644
--- a/tools/host/src/Android.mk
+++ b/tools/host/src/Android.mk
@@ -21,7 +21,7 @@
 
 LOCAL_JAR_MANIFEST := ../etc/manifest.txt
 LOCAL_JAVA_LIBRARIES := \
-    ddmlib-prebuilt junit hosttestlib CtsTestAnnotationsHostLib
+    ddmlib-prebuilt junit hosttestlib
 
 LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceinfolib
 
diff --git a/tools/host/src/com/android/cts/ConsoleUi.java b/tools/host/src/com/android/cts/ConsoleUi.java
index c9b0e1d..ce26d52 100644
--- a/tools/host/src/com/android/cts/ConsoleUi.java
+++ b/tools/host/src/com/android/cts/ConsoleUi.java
@@ -80,7 +80,6 @@
         mResultCodeMap.put(CtsTestResult.STR_ERROR, CtsTestResult.CODE_ERROR);
         mResultCodeMap.put(CtsTestResult.STR_NOT_EXECUTED, CtsTestResult.CODE_NOT_EXECUTED);
         mResultCodeMap.put(CtsTestResult.STR_TIMEOUT, CtsTestResult.CODE_TIMEOUT);
-        mResultCodeMap.put(CtsTestResult.STR_OMITTED, CtsTestResult.CODE_OMITTED);
     }
 
     public ConsoleUi(TestHost host) {
@@ -1183,9 +1182,9 @@
             CUIOutputStream.println("There aren't any test results!");
         } else {
             CUIOutputStream.println("List of all results: ");
-            CUIOutputStream.println("Session\t\tTest result\t\t\t\t\tStart time\t\tEnd time\t"
+            CUIOutputStream.println("Session\t\tTest result\t\t\t\tStart time\t\tEnd time\t"
                     + "\tTest plan name\t");
-            CUIOutputStream.println("\t\tPass\tFail\tTimeout\tOmitted\tNotExecuted");
+            CUIOutputStream.println("\t\tPass\tFail\tTimeout\tNotExecuted");
 
             for (TestSession session : sessions) {
                 TestSessionLog log = session.getSessionLog();
@@ -1193,8 +1192,6 @@
                         CtsTestResult.CODE_PASS).size();
                 int failNum = log.getTestList(
                         CtsTestResult.CODE_FAIL).size();
-                int omittedNum = log.getTestList(
-                        CtsTestResult.CODE_OMITTED).size();
                 int notExecutedNum = log.getTestList(
                         CtsTestResult.CODE_NOT_EXECUTED).size();
                 int timeOutNum = log.getTestList(
@@ -1202,7 +1199,6 @@
 
                 String resStr = Long.toString(passNum) + "\t" + failNum;
                 resStr += "\t" + timeOutNum;
-                resStr += "\t" + omittedNum;
                 resStr += "\t" + notExecutedNum;
 
                 String startTimeStr =
diff --git a/tools/host/src/com/android/cts/CtsTestResult.java b/tools/host/src/com/android/cts/CtsTestResult.java
index 851b07d..b64863d 100644
--- a/tools/host/src/com/android/cts/CtsTestResult.java
+++ b/tools/host/src/com/android/cts/CtsTestResult.java
@@ -37,14 +37,12 @@
     public static final int CODE_FAIL = 2;
     public static final int CODE_ERROR = 3;
     public static final int CODE_TIMEOUT = 4;
-    public static final int CODE_OMITTED = 5;
     public static final int CODE_FIRST = CODE_INIT;
-    public static final int CODE_LAST = CODE_OMITTED;
+    public static final int CODE_LAST = CODE_TIMEOUT;
 
     public static final String STR_ERROR = "error";
     public static final String STR_TIMEOUT = "timeout";
     public static final String STR_NOT_EXECUTED = "notExecuted";
-    public static final String STR_OMITTED = "omitted";
     public static final String STR_FAIL = "fail";
     public static final String STR_PASS = "pass";
 
@@ -57,7 +55,6 @@
         sCodeToResultMap.put(CODE_FAIL, STR_FAIL);
         sCodeToResultMap.put(CODE_ERROR, STR_ERROR);
         sCodeToResultMap.put(CODE_TIMEOUT, STR_TIMEOUT);
-        sCodeToResultMap.put(CODE_OMITTED, STR_OMITTED);
         sResultToCodeMap = new HashMap<String, Integer>();
         for (int code : sCodeToResultMap.keySet()) {
             sResultToCodeMap.put(sCodeToResultMap.get(code), code);
diff --git a/tools/host/src/com/android/cts/TestDevice.java b/tools/host/src/com/android/cts/TestDevice.java
index e72b97c..6ac56a1 100644
--- a/tools/host/src/com/android/cts/TestDevice.java
+++ b/tools/host/src/com/android/cts/TestDevice.java
@@ -27,8 +27,8 @@
 import com.android.ddmlib.ShellCommandUnresponsiveException;
 import com.android.ddmlib.SyncException;
 import com.android.ddmlib.SyncService;
-import com.android.ddmlib.SyncService.ISyncProgressMonitor;
 import com.android.ddmlib.TimeoutException;
+import com.android.ddmlib.SyncService.ISyncProgressMonitor;
 import com.android.ddmlib.log.LogReceiver;
 import com.android.ddmlib.log.LogReceiver.ILogListener;
 
@@ -1452,10 +1452,6 @@
             case STATUS_ERROR:
                 mResultCode = CtsTestResult.CODE_FAIL;
                 break;
-
-            case STATUS_OMITTED:
-                mResultCode = CtsTestResult.CODE_OMITTED;
-                break;
             }
         }
 
@@ -1521,10 +1517,6 @@
                 case STATUS_PASS:
                     mResultCode = CtsTestResult.CODE_PASS;
                     break;
-
-                case STATUS_OMITTED:
-                    mResultCode = CtsTestResult.CODE_OMITTED;
-                    break;
                 }
                 resultLines.removeAll(resultLines);
             }
@@ -1576,10 +1568,6 @@
                     mTest.setResult(new CtsTestResult(
                             CtsTestResult.CODE_FAIL, mFailedMsg, mStackTrace));
                     break;
-
-                case STATUS_OMITTED:
-                    mTest.setResult(new CtsTestResult(CtsTestResult.CODE_OMITTED));
-                    break;
                 }
             }
             // report status even if no matching test was found
diff --git a/tools/host/src/com/android/cts/TestSession.java b/tools/host/src/com/android/cts/TestSession.java
index fedd756..e3693d8 100644
--- a/tools/host/src/com/android/cts/TestSession.java
+++ b/tools/host/src/com/android/cts/TestSession.java
@@ -485,15 +485,13 @@
         private void displayTestResultSummary() {
             int passNum = mSessionLog.getTestList(CtsTestResult.CODE_PASS).size();
             int failNum = mSessionLog.getTestList(CtsTestResult.CODE_FAIL).size();
-            int omittedNum = mSessionLog.getTestList(CtsTestResult.CODE_OMITTED).size();
             int notExecutedNum = mSessionLog.getTestList(CtsTestResult.CODE_NOT_EXECUTED).size();
             int timeOutNum = mSessionLog.getTestList(CtsTestResult.CODE_TIMEOUT).size();
-            int total = passNum + failNum + omittedNum + notExecutedNum + timeOutNum;
+            int total = passNum + failNum + notExecutedNum + timeOutNum;
 
             println("Test summary:   pass=" + passNum
                     + "   fail=" + failNum
                     + "   timeOut=" + timeOutNum
-                    + "   omitted=" + omittedNum
                     + "   notExecuted=" + notExecutedNum
                     + "   Total=" + total);
         }
diff --git a/tools/host/src/com/android/cts/TestSessionLog.java b/tools/host/src/com/android/cts/TestSessionLog.java
index 0b53076..bf5b3e8 100644
--- a/tools/host/src/com/android/cts/TestSessionLog.java
+++ b/tools/host/src/com/android/cts/TestSessionLog.java
@@ -78,7 +78,6 @@
     static final String ATTRIBUTE_PASS = "pass";
     static final String ATTRIBUTE_FAILED = "failed";
     static final String ATTRIBUTE_TIMEOUT = "timeout";
-    static final String ATTRIBUTE_OMITTED = "omitted";
     static final String ATTRIBUTE_NOT_EXECUTED = "notExecuted";
 
     static final String TAG_DEVICEINFO = "DeviceInfo";
@@ -385,14 +384,12 @@
 
             int passNum = getTestList(CtsTestResult.CODE_PASS).size();
             int failNum = getTestList(CtsTestResult.CODE_FAIL).size();
-            int omittedNum = getTestList(CtsTestResult.CODE_OMITTED).size();
             int notExecutedNum = getTestList(CtsTestResult.CODE_NOT_EXECUTED).size();
             int timeOutNum = getTestList(CtsTestResult.CODE_TIMEOUT).size();
             Node summaryNode = doc.createElement(TAG_SUMMARY);
             root.appendChild(summaryNode);
             setAttribute(doc, summaryNode, ATTRIBUTE_PASS, passNum);
             setAttribute(doc, summaryNode, ATTRIBUTE_FAILED, failNum);
-            setAttribute(doc, summaryNode, ATTRIBUTE_OMITTED, omittedNum);
             setAttribute(doc, summaryNode, ATTRIBUTE_NOT_EXECUTED, notExecutedNum);
             setAttribute(doc, summaryNode, ATTRIBUTE_TIMEOUT, timeOutNum);
 
diff --git a/tools/host/src/com/android/cts/Version.java b/tools/host/src/com/android/cts/Version.java
index f2c83b9..2d46d45 100644
--- a/tools/host/src/com/android/cts/Version.java
+++ b/tools/host/src/com/android/cts/Version.java
@@ -18,7 +18,7 @@
 
 public class Version {
     // The CTS version string
-    private static final String version = "3.0_r1";
+    private static final String version = "3.0_r2";
 
     private Version() {
         // no instances allowed
diff --git a/tools/spec-progress/Android.mk b/tools/spec-progress/Android.mk
deleted file mode 100644
index e4026e9..0000000
--- a/tools/spec-progress/Android.mk
+++ /dev/null
@@ -1,15 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_IS_HOST_MODULE := true
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_MODULE := spec-progress
-
-include $(BUILD_SYSTEM)/base_rules.mk
-
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/spec-progress | $(ACP)
-	@echo "Copy: $(PRIVATE_MODULE) ($@)"
-	$(copy-file-to-new-target)
-	$(hide) chmod 755 $@
-
-include $(LOCAL_PATH)/src/Android.mk
diff --git a/tools/spec-progress/etc/spec-progress b/tools/spec-progress/etc/spec-progress
deleted file mode 100644
index a8f14d7..0000000
--- a/tools/spec-progress/etc/spec-progress
+++ /dev/null
@@ -1,154 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2008 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.
-
-# Set up prog to be the path of this script, including following symlinks,
-# and set up progdir to be the fully-qualified pathname of its directory.
-prog="$0"
-while [ -h "${prog}" ]; do
-    newProg=`/bin/ls -ld "${prog}"`
-    newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
-    if expr "x${newProg}" : 'x/' >/dev/null; then
-        prog="${newProg}"
-    else
-        progdir=`dirname "${prog}"`
-        prog="${progdir}/${newProg}"
-    fi
-done
-oldwd=`pwd`
-progdir=`dirname "${prog}"`
-cd "${progdir}"
-progdir=`pwd`
-prog="${progdir}"/`basename "${prog}"`
-cd "${oldwd}"
-
-libdir=`dirname $progdir`/framework
-
-javaOpts=""
-while expr "x$1" : 'x-J' >/dev/null; do
-    opt=`expr "$1" : '-J\(.*\)'`
-    javaOpts="${javaOpts} -${opt}"
-    shift
-done
-
-#exec java $javaOpts -jar $libdir/hat.jar "$@"
-
-#######################################################################
-# Original content of invocation script follows. Uses values cleverly
-# deduced by the above code. If you want to use this for a different
-# set of packages, adjust both the list of source directories and the
-# list of packages.
-#######################################################################
-export CLASSES=$progdir/../framework/spec-progress.jar
-export INPUT=$ANDROID_BUILD_TOP
-export OUTPUT=$ANDROID_BUILD_TOP/out/target/common/cts/spec-progress
-
-if [ "$1" != "" ]; then
-   export OUTPUT=$1
-fi
-
-javadoc -J-Xmx512m -docletpath $CLASSES -doclet SpecProgressDoclet -d $OUTPUT -sourcepath \
-$INPUT/dalvik/libcore/dalvik/src/main/java:\
-$INPUT/dalvik/libcore/annotation/src/main/java:\
-$INPUT/dalvik/libcore/archive/src/main/java:\
-$INPUT/dalvik/libcore/auth/src/main/java:\
-$INPUT/dalvik/libcore/awt-kernel/src/main/java:\
-$INPUT/dalvik/libcore/beans/src/main/java:\
-$INPUT/dalvik/libcore/crypto/src/main/java:\
-$INPUT/dalvik/libcore/logging/src/main/java:\
-$INPUT/dalvik/libcore/luni/src/main/java:\
-$INPUT/dalvik/libcore/luni-kernel/src/main/java:\
-$INPUT/dalvik/libcore/math/src/main/java:\
-$INPUT/dalvik/libcore/nio/src/main/java:\
-$INPUT/dalvik/libcore/nio_char/src/main/java:\
-$INPUT/dalvik/libcore/prefs/src/main/java:\
-$INPUT/dalvik/libcore/regex/src/main/java:\
-$INPUT/dalvik/libcore/security/src/main/java:\
-$INPUT/dalvik/libcore/security-kernel/src/main/java:\
-$INPUT/dalvik/libcore/sql/src/main/java:\
-$INPUT/dalvik/libcore/text/src/main/java:\
-$INPUT/dalvik/libcore/xml/src/main/java:\
-$INPUT/dalvik/libcore/x-net/src/main/java:\
- \
-dalvik.annotation \
-dalvik.bytecode \
-dalvik.system \
-java.io \
-java.lang \
-java.lang.annotation \
-java.lang.ref \
-java.lang.reflect \
-java.math \
-java.net \
-java.nio \
-java.nio.channels \
-java.nio.channels.spi \
-java.nio.charset \
-java.nio.charset.spi \
-java.security \
-java.security.acl \
-java.security.cert \
-java.security.interfaces \
-java.security.spec \
-java.sql \
-java.text \
-java.util \
-java.util.jar \
-java.util.logging \
-java.util.prefs \
-java.util.regex \
-java.util.zip \
-javax.crypto \
-javax.crypto.interfaces \
-javax.crypto.spec \
-javax.net \
-javax.net.ssl \
-javax.security.auth \
-javax.security.auth.callback \
-javax.security.auth.login \
-javax.security.auth.x500 \
-javax.security.cert \
-javax.sql \
-javax.xml.parsers \
-org.w3c.dom \
-org.xml.sax \
-org.xml.sax.ext \
-org.xml.sax.helpers \
-
-# Not part of core libs any more:
-# java.lang.instrument \
-# javax.sound.midi \
-# javax.sound.midi.spi \
-# javax.sound.sampled \
-# javax.sound.sampled.spi \
-# java.awt \
-# java.awt.color \
-# java.awt.event \
-# java.awt.font \
-# java.awt.geom \
-# java.awt.im \
-# java.awt.im.spi \
-# java.awt.image \
-# java.awt.image.renderable \
-# javax.imageio \
-# javax.imageio.event \
-# javax.imageio.metadata \
-# javax.imageio.plugins.bmp \
-# javax.imageio.plugins.jpeg \
-# javax.imageio.spi \
-# javax.imageio.stream \
-# java.util.concurrent \
-# java.util.concurrent.atomic \
-# java.util.concurrent.locks \
diff --git a/tools/spec-progress/src/Android.mk b/tools/spec-progress/src/Android.mk
deleted file mode 100644
index 8ab5a7b..0000000
--- a/tools/spec-progress/src/Android.mk
+++ /dev/null
@@ -1,14 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-#LOCAL_MODULE_TAGS := cts
-
-LOCAL_SRC_FILES := \
-	SpecProgressDoclet.java
-
-LOCAL_CLASSPATH := \
-	$(HOST_JDK_TOOLS_JAR)
-
-LOCAL_MODULE:= spec-progress
-
-include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tools/spec-progress/src/SpecProgressDoclet.java b/tools/spec-progress/src/SpecProgressDoclet.java
deleted file mode 100644
index 4c5b6cc..0000000
--- a/tools/spec-progress/src/SpecProgressDoclet.java
+++ /dev/null
@@ -1,1074 +0,0 @@
-/*
- * Copyright (C) 2008 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.
- */
-
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import com.sun.javadoc.ClassDoc;
-import com.sun.javadoc.ConstructorDoc;
-import com.sun.javadoc.Doc;
-import com.sun.javadoc.ExecutableMemberDoc;
-import com.sun.javadoc.FieldDoc;
-import com.sun.javadoc.LanguageVersion;
-import com.sun.javadoc.MemberDoc;
-import com.sun.javadoc.MethodDoc;
-import com.sun.javadoc.PackageDoc;
-import com.sun.javadoc.ParamTag;
-import com.sun.javadoc.Parameter;
-import com.sun.javadoc.RootDoc;
-import com.sun.javadoc.Tag;
-import com.sun.javadoc.ThrowsTag;
-import com.sun.javadoc.TypeVariable;
-
-/**
- * Provides a Doclet for checking the correctness and completeness of the
- * Android core library JavaDoc (aka "the spec"). It generates an HTML-based
- * report vaguely similar to the standard JavaDoc output. The following rules
- * are currently implemented:
- * 
- * Each package must have a package.html doc, and all classes must be documented
- * as described below.
- * 
- * Each class must have an individual doc and all members (fields, constructors,
- * methods) must be documented as described below. All type parameters on class 
- * level need to be documented.
- * 
- * Each member must have an individual doc.
- * 
- * Each executable member (constructor or method) must have a "@param" tag
- * describing each declared parameter. "@param" tags for non-existing parameters
- * are not allowed.
- * 
- * Each method that has a non-void return type must have at least one "@return"
- * tag. A method that has a void return type must not have a "@return" tag.
- * 
- * Each executable member must have a "@throws" tag for each declared exception
- * that does not extend java.lang.RuntimeException or java.lang.Error. This
- * tag may refer to a superclass of the exception actually being thrown. Each
- * exception specified by a "@throws" tag must actually be declared by the
- * member, unless it extends java.lang.RuntimeException or java.lang.Error.
- * Again, the exception being thrown might be more specific than the one
- * documented.
- * 
- * Methods that override or implement another method are allowed to be
- * undocumented, resulting in the inherited documentation being used. If such a
- * method is documented anyway, it must have the complete documentation as
- * described above.
- * 
- * Elements that have a "@hide" JavaDoc tag are not considered part of the
- * official API and hence are not required to be documented.
- * 
- * Based on checking the above rules, the Doclet assigns statuses to individual
- * documentation elements as follows:
- * 
- * Red: the element violates at least one of the above rules.
- * 
- * Yellow: the element fulfills all the above rules, but neither it nor one of
- * its parent elements (class, package) has been marked with the
- * "@since Android-1.0" tag.
- * 
- * Green: the element fulfills all the above rules, it does not have any "@cts"
- * tags, and either it or one if its parent elements (class, package) has been
- * marked with the "@since Android-1.0" tag.
- * 
- * These colors propagate upwards in the hierarchy. Parent elements are assigned
- * colors as follows:
- * 
- * Red: At least on the children is red.
- * 
- * Yellow: None of the children are red and at least one of the children is
- * yellow.
- * 
- * Green: All of the children are green.
- * 
- * The ultimate goal, of course, is to get the summary for the complete API
- * green.
- */
-public class SpecProgressDoclet {
-
-    public static final int TYPE_FIELD = 0;
-
-    public static final int TYPE_METHOD = 1;
-
-    public static final int TYPE_CLASS = 2;
-
-    public static final int TYPE_PACKAGE = 3;
-
-    public static final int TYPE_ROOT = 4;
-
-    public static final int VALUE_RED = 0;
-
-    public static final int VALUE_YELLOW = 1;
-
-    public static final int VALUE_GREEN = 2;
-
-    public static final String[] COLORS = { "#ffa0a0", "#ffffa0", "#a0ffa0" };
-
-    public static final String[] TYPES = { "Field", "Method", "Class",
-                                           "Package", "All packages" };
-
-    /**
-     * Holds our basic output directory.
-     */
-    private File directory;
-
-    /**
-     * Holds a reference to the doc for java.lang.RuntimeException, so we can
-     * compare against it later.
-     */
-    private ClassDoc runtimeException;
-
-    /**
-     * Holds a reference to the doc for java.lang.Error, so we can
-     * compare against it later.
-     */
-    private ClassDoc error;
-
-    /**
-     * States whether to check type parameters on class level. 
-     * To enable these checks use the option: '-Xgeneric' 
-     */
-    private static boolean checkTypeParameters;
-
-    /**
-     * Helper class for comparing element with each other, in oder to determine
-     * an order. Uses lexicographic order of names.
-     */
-    private class DocComparator implements Comparator<Doc> {
-        public int compare(Doc elem1, Doc elem2) {
-            return elem1.name().compareTo(elem2.name());
-        }
-
-        public boolean equals(Doc elem) {
-            return this == elem;
-        }
-    }
-
-    /**
-     * Class for collecting stats and propagating them upwards in the element
-     * hierarchy.
-     */
-    class Stats {
-
-        /**
-         * Holds the element type.
-         */
-        int type;
-
-        /**
-         * Holds the name of the element.
-         */
-        String name;
-
-        /**
-         * Holds information that is sufficient for building a hyperlink.
-         */
-        String link;
-
-        /**
-         * Holds the total number of elements per type (package, class, etc.).
-         */
-        private int[] numbersPerType = new int[4];
-
-        /**
-         * Holds the total number of elements per status value (red, yellow,
-         * green).
-         */
-        private int[] numbersPerValue = new int[3];
-
-        /**
-         * Holds the total number of "@cts" comments.
-         */
-        private int numberOfComments;
-
-        /**
-         * Creates a new Stats instance.
-         */
-        public Stats(int type, String name, String link) {
-            this.type = type;
-            this.name = name;
-            this.link = link;
-        }
-
-        /**
-         * Adds the contents of a single child element to this instance,
-         * propagating values up in the hierachy
-         */
-        public void add(int type, int status, int comments) {
-            numbersPerType[type]++;
-            numbersPerValue[status]++;
-            numberOfComments += comments;
-        }
-
-        /**
-         * Adds the contents of a child Stats instance to this instance,
-         * propagating values up in the hierachy
-         */
-        public void add(Stats stats) {
-            for (int i = 0; i < numbersPerType.length; i++) {
-                numbersPerType[i] += stats.numbersPerType[i];
-            }
-
-            for (int i = 0; i < numbersPerValue.length; i++) {
-                numbersPerValue[i] += stats.numbersPerValue[i];
-            }
-
-            numberOfComments += stats.numberOfComments;
-        }
-
-        /**
-         * Returns the link.
-         */
-        public String getLink() {
-            return link;
-        }
-
-        /**
-         * Returns the name.
-         */
-        public String getName() {
-            return name;
-        }
-
-        /**
-         * Returns the number of elements per element type.
-         */
-        public int getNumbersPerType(int type) {
-            return numbersPerType[type];
-        }
-
-        /**
-         * Returns the number of elements per status value.
-         */
-        public int getNumbersPerValue(int type) {
-            return numbersPerValue[type];
-        }
-
-        /**
-         * Returns the number of comments.
-         */
-        public int getNumberOfComments() {
-            return numberOfComments;
-        }
-
-        /**
-         * Returns the type of the element to which this Stats instance belongs.
-         */
-        public int getType() {
-            return type;
-        }
-
-        /**
-         * Returns the accumulated status value.
-         */
-        public int getValue() {
-            if (numbersPerValue[VALUE_RED] != 0) {
-                return VALUE_RED;
-            } else if (numbersPerValue[VALUE_YELLOW] != 0) {
-                return VALUE_YELLOW;
-            } else {
-                return VALUE_GREEN;
-            }
-        }
-
-    }
-
-    /**
-     * Holds our comparator instance for everything.
-     */
-    private DocComparator comparator = new DocComparator();
-
-    /**
-     * Creates a new instance of the SpecProgressDoclet for a given target
-     * directory.
-     */
-    public SpecProgressDoclet(String directory) {
-        this.directory = new File(directory);
-    }
-
-    /**
-     * Opens a new output file and writes the usual HTML header. Directories
-     * are created on demand.
-     */
-    private PrintWriter openFile(String name, String title) throws IOException {
-        System.out.println("Writing file \"" + name + "\"...");
-        
-        File file = new File(directory, name);
-        File parent = file.getParentFile();
-        parent.mkdirs();
-
-        OutputStream stream = new BufferedOutputStream(new FileOutputStream(file));
-        PrintWriter printer = new PrintWriter(stream);
-
-        printer.println("<html>");
-        printer.println("  <head>");
-        printer.println("    <title>" + title + "</title>");
-        printer.println("  <head>");
-        printer.println("  <body>");
-        printer.println("    <h1>" + title + "</h1>");
-
-        return printer;
-    }
-
-    /**
-     * Closes the given output file, writing the usual HTML footer before.
-     */
-    private void closeFile(PrintWriter printer) {
-        printer.println("  </body>");
-        printer.println("</html>");
-
-        printer.flush();
-        printer.close();
-    }
-
-    /**
-     * Processes the whole list of classes that JavaDoc knows about.
-     */
-    private void process(RootDoc root) throws IOException {
-        runtimeException = root.classNamed("java.lang.RuntimeException");
-        error = root.classNamed("java.lang.Error");
-
-        PrintWriter printer = openFile("index.html", "All packages");
-
-        printer.println("Generated " + new Date().toString());
-        
-        Stats derived = new Stats(TYPE_ROOT, "All packages", null);
-
-        printer.println("      <h2>Children</h2>");
-        printer.println("      <table width=\"100%\">");
-        printStatsHeader(printer);
-
-        PackageDoc[] packages = root.specifiedPackages();
-        Arrays.sort(packages, comparator);
-        for (PackageDoc pack : packages) {
-            if (pack.allClasses().length != 0 && !isHidden(pack)) {
-                Stats subStats = processPackage(pack);
-                printStats(printer, subStats, true);
-                derived.add(subStats);
-            }
-        }
-
-        printer.println("      </table>");
-
-        printer.println("      <p>");
-
-        printer.println("      <h2>Summary</h2>");
-        printer.println("      <table width=\"100%\">");
-        printStatsHeader(printer);
-        printStats(printer, derived, false);
-        printer.println("      </table>");
-
-        closeFile(printer);
-    }
-
-    /**
-     * Processes the details of a single package.
-     */
-    private Stats processPackage(PackageDoc pack) throws IOException {
-        String file = getPackageDir(pack) + "/package.html";
-        PrintWriter printer = openFile(file, "Package " + pack.name());
-
-        Stats derived = new Stats(TYPE_PACKAGE, pack.name(), file);
-
-        printer.println("      <h2>Elements</h2>");
-        printer.println("      <table width=\"100%\">");
-
-        printElementHeader(printer);
-        processElement(printer, pack, TYPE_PACKAGE, derived);
-
-        printer.println("      </table>");
-
-        printer.println("      <p>");
-
-        printer.println("      <h2>Children</h2>");
-        printer.println("      <table width=\"100%\">");
-
-        printStatsHeader(printer);
-
-        ClassDoc[] classes = pack.allClasses();
-        Arrays.sort(classes, comparator);
-        for (ClassDoc clazz : classes) {
-            if (!isHidden(clazz)) {
-                Stats subStats = processClass(clazz);
-                printStats(printer, subStats, true);
-                derived.add(subStats);
-            }
-        }
-
-        printer.println("      </table>");
-
-        printer.println("      <h2>Summary</h2>");
-        printer.println("      <table width=\"100%\">");
-        printStatsHeader(printer);
-        printStats(printer, derived, false);
-        printer.println("      </table>");
-
-        closeFile(printer);
-
-        return derived;
-    }
-
-    /**
-     * Processes the details of a single class.
-     */
-    private Stats processClass(ClassDoc clazz) throws IOException {
-        String file = getPackageDir(clazz.containingPackage()) + "/" + clazz.name() + ".html";
-        PrintWriter printer = openFile(file, "Class " + clazz.name());
-
-        Stats derived = new Stats(TYPE_CLASS, clazz.name(), clazz.name() + ".html");
-
-        printer.println("      <h2>Elements</h2>");
-        printer.println("      <table width=\"100%\">");
-
-        printElementHeader(printer);
-
-        processElement(printer, clazz, TYPE_CLASS, derived);
-
-        if(clazz.isEnum()){
-            FieldDoc[] enums = clazz.enumConstants();
-            Arrays.sort(enums, comparator);
-            for(FieldDoc e : enums) {
-                processElement(printer, e, TYPE_FIELD, derived);
-            }
-        }
-
-        FieldDoc[] fields = clazz.fields();
-        Arrays.sort(fields, comparator);
-        for (FieldDoc field : fields) {
-            processElement(printer, field, TYPE_FIELD, derived);
-        }
-
-        ConstructorDoc[] constructors = clazz.constructors();
-        Arrays.sort(constructors, comparator);
-        for (ConstructorDoc constructor : constructors) {
-            if (constructor.position() != null) {
-                String constPos = constructor.position().toString();
-                String classPos = constructor.containingClass().position()
-                        .toString();
-
-                if (!constPos.equals(classPos)) {
-                    processElement(printer, constructor, TYPE_METHOD, derived);
-                }
-            }
-        }
-
-        HashSet<MethodDoc> methodSet = new HashSet<MethodDoc>();
-        
-        
-        ClassDoc superClass = clazz.superclass();
-        MethodDoc[] methods = null;
-        if (superClass != null && superClass.isPackagePrivate())
-        {
-            MethodDoc[] classMethods = clazz.methods();
-            for (int i = 0; i < classMethods.length; i++) {
-                methodSet.add(classMethods[i]);
-            }
-
-
-            while (superClass != null && superClass.isPackagePrivate()) {
-                classMethods = superClass.methods();
-                for (int i = 0; i < classMethods.length; i++) {
-                    methodSet.add(classMethods[i]);
-                }
-                superClass = superClass.superclass();
-            }
-
-            methods = new MethodDoc[methodSet.size()];
-            methodSet.toArray(methods);
-        }
-        else
-        {
-            methods = clazz.methods();
-        }
-
-        Arrays.sort(methods, comparator);
-        for (MethodDoc method : methods) {
-            if (!(clazz.isEnum() && ("values".equals(method.name()) ||
-                                     "valueOf".equals(method.name())))) {
-                processElement(printer, method, TYPE_METHOD, derived);
-            }
-        }
-
-        printer.println("      </table>");
-
-        printer.println("      <p>");
-
-        printer.println("      <h2>Summary</h2>");
-        printer.println("      <table width=\"100%\">");
-        printStatsHeader(printer);
-        printStats(printer, derived, false);
-        printer.println("      </table>");
-
-        closeFile(printer);
-
-        return derived;
-    }
-
-    /**
-     * Processes a single element.
-     */
-    private void processElement(PrintWriter printer, Doc doc, int type, Stats derived) {
-        if (isHidden(doc)) {
-            return;
-        }
-
-        List<String> errors = new ArrayList<String>();
-        
-        boolean documented = isValidComment(doc.commentText());
-        boolean inherited = false;
-
-        if(checkTypeParameters && (doc.isClass() || doc.isInterface())){
-            boolean typeParamsOk = hasAllTypeParameterDocs((ClassDoc)doc, errors);
-            documented = documented && typeParamsOk;
-        }
-
-        if (doc.isMethod()) {
-            MethodDoc method = (MethodDoc) doc;
-            
-            if ("".equals(method.commentText().trim())) {
-                inherited = method.overriddenMethod() != null || 
-                            implementedMethod(method) != null;
-                documented = inherited;
-            }
-        }
-
-        if (!documented) {
-            errors.add("Missing or insufficient doc.");
-        }
-        
-        if (!inherited) {
-            if (doc.isMethod() || doc.isConstructor()) {
-                ExecutableMemberDoc executable = (ExecutableMemberDoc) doc;
-                boolean paramsOk = hasAllParameterDocs(executable, errors);
-                boolean exceptionsOk = hasAllExceptionDocs(executable, errors);
-
-                documented = documented && paramsOk && exceptionsOk;
-            }
-
-            if (doc.isMethod()) {
-                MethodDoc method = (MethodDoc) doc;
-                boolean resultOk = hasReturnDoc(method, errors);
-                documented = documented && resultOk;
-            }
-        }
-        
-        boolean reviewed = hasSinceTag(doc);
-        Tag[] comments = doc.tags("cts");
-
-        int status = getStatus(documented, reviewed || inherited, comments);
-
-        printer.println("        <tr bgcolor=\"" + COLORS[status] + "\">");
-        printer.println("          <td>" + TYPES[type] + "</td>");
-        
-        if (doc instanceof PackageDoc) {
-            printer.println("          <td>" + doc.toString() + "</td>");
-        } else {
-            String s = doc.name();
-            String t = doc.toString();
-            
-            int i = t.indexOf(s);
-            
-            if (i != -1) {
-                t = t.substring(i);
-            }
-            
-            printer.println("          <td>" + t + "</td>");
-        }
-        
-        printer.println("          <td>" + getFirstSentence(doc) + "</td>");
-        printer.println("          <td>" + (documented ? "Yes" : "No") + "</td>");
-        printer.println("          <td>" + (reviewed ? "Yes" : "No") + "</td>");
-        printer.println("          <td>");
-
-        if (comments.length != 0 || errors.size() != 0) {
-            printer.println("            </ul>");
-
-            for (int i = 0; i < comments.length; i++) {
-                printer.print("              <li>");
-                printer.print(comments[i].text());
-                printer.println("</li>");
-            }
-            
-            for (int i = 0; i < errors.size(); i++) {
-                printer.print("              <li>");
-                printer.print(errors.get(i));
-                printer.println("</li>");
-            }
-
-            printer.println("            </ul>");
-        } else {
-            printer.println("&nbsp;");
-        }
-
-        printer.println("          </td>");
-        printer.println("        </tr>");
-
-        derived.add(type, status, comments.length);
-    }
-
-    /**
-     * Print the table header for an element table.
-     */
-    private void printElementHeader(PrintWriter printer) {
-        printer.println("        <tr>");
-        printer.println("          <td>Type</td>");
-        printer.println("          <td>Name</td>");
-        printer.println("          <td>First sentence</td>");
-        printer.println("          <td>Doc'd</td>");
-        printer.println("          <td>Rev'd</td>");
-        printer.println("          <td>Comments</td>");
-        printer.println("        </tr>");
-    }
-
-    /**
-     * Print the table header for stats table table.
-     */
-    private void printStatsHeader(PrintWriter printer) {
-        printer.println("        <tr>");
-        printer.println("          <td>Type</td>");
-        printer.println("          <td>Name</td>");
-        printer.println("          <td>#Classes</td>");
-        printer.println("          <td>#Fields</td>");
-        printer.println("          <td>#Methods</td>");
-        printer.println("          <td>#Red</td>");
-        printer.println("          <td>#Yellow</td>");
-        printer.println("          <td>#Green</td>");
-        printer.println("          <td>#Comments</td>");
-        printer.println("        </tr>");
-    }
-
-    /**
-     * Prints a single row to a stats table.
-     */
-    private void printStats(PrintWriter printer, Stats info, boolean wantLink) {
-        printer.println("        <tr bgcolor=\"" + COLORS[info.getValue()] + "\">");
-        printer.println("          <td>" + TYPES[info.getType()] + "</td>");
-
-        printer.print("          <td>");
-        String link = info.getLink();
-        if (wantLink && link != null) {
-            printer.print("<a href=\"" + link + "\">" + info.getName() + "</a>");
-        } else {
-            printer.print(info.getName());
-        }
-        printer.println("</td>");
-
-        printer.println("          <td>" + info.getNumbersPerType(TYPE_CLASS) + "</td>");
-        printer.println("          <td>" + info.getNumbersPerType(TYPE_FIELD) + "</td>");
-        printer.println("          <td>" + info.getNumbersPerType(TYPE_METHOD) + "</td>");
-        printer.println("          <td>" + info.getNumbersPerValue(VALUE_RED) + "</td>");
-        printer.println("          <td>" + info.getNumbersPerValue(VALUE_YELLOW) + "</td>");
-        printer.println("          <td>" + info.getNumbersPerValue(VALUE_GREEN) + "</td>");
-        printer.println("          <td>" + info.getNumberOfComments() + "</td>");
-        printer.println("        </tr>");
-    }
-
-    /**
-     * Returns the directory for a given package. Basically converts embedded
-     * dots in the name into slashes. 
-     */
-    private File getPackageDir(PackageDoc pack) {
-        if (pack == null || pack.name() == null || "".equals(pack.name())) {
-            return new File(".");
-        } else {
-            return new File(pack.name().replace('.', '/'));
-        }
-    }
-
-    /**
-     * Checks whether the given comment is not null and not of length 0.
-     */
-    private boolean isValidComment(String comment) {
-        return comment != null && comment.length() > 0;
-    }
-
-    /**
-     * Checks whether the given interface or class has documentation for
-     * all declared type parameters (no less, no more). 
-     */
-    private boolean hasAllTypeParameterDocs(ClassDoc doc, List<String> errors) {
-        boolean result = true;
-        
-        TypeVariable[] params = doc.typeParameters();
-        Set<String> paramsSet = new HashSet<String>();
-        for (TypeVariable param : params) {
-            paramsSet.add(param.typeName()); 
-        }  
-
-        ParamTag[] paramTags = doc.typeParamTags();
-        Map<String, String> paramTagsMap = new HashMap<String, String>();
-        for (ParamTag paramTag : paramTags) {
-            if (!paramsSet.contains(paramTag.parameterName())) {
-                errors.add("Unknown type parameter \"" + paramTag.parameterName() + "\"");
-                result = false;
-            }
-            paramTagsMap.put(paramTag.parameterName(), paramTag.parameterComment());
-        }
-
-        for (TypeVariable param : params) {
-            if (!isValidComment(paramTagsMap.get(param.typeName()))) {
-                errors.add("Undocumented type parameter \"" + param.typeName() + "\"");
-                result = false;
-            }
-        }
-
-        return result;
-    }
-
-    /**
-     * Checks whether the given executable member has documentation for
-     * all declared parameters (no less, no more).  
-     */
-    private boolean hasAllParameterDocs(ExecutableMemberDoc doc, List<String> errors) {
-        boolean result = true;
-        
-        Parameter params[] = doc.parameters();
-        Set<String> paramsSet = new HashSet<String>();
-        for (int i = 0; i < params.length; i++) {
-            Parameter param = params[i];
-            paramsSet.add(param.name());
-        }
-
-        ParamTag[] paramTags = doc.paramTags();
-        Map<String, String> paramTagsMap = new HashMap<String, String>();
-        for (int i = 0; i < paramTags.length; i++) {
-            ParamTag paramTag = paramTags[i];
-
-            if (!paramsSet.contains(paramTag.parameterName())) {
-                errors.add("Unknown parameter \"" + paramTag.parameterName() + "\"");
-                result = false;
-            }
-
-            paramTagsMap.put(paramTag.parameterName(), paramTag.parameterComment());
-        }
-
-        for (int i = 0; i < params.length; i++) {
-            Parameter param = params[i];
-
-            if (!isValidComment(paramTagsMap.get(param.name()))) {
-                errors.add("Undocumented parameter \"" + param.name() + "\"");
-                result = false;
-            }
-        }
-
-        return result;
-    }
-
-    /**
-     * Checks whether the given executable member has documentation for
-     * all non-runtime exceptions. Runtime exceptions may or may not be
-     * documented.   
-     */
-    private boolean hasAllExceptionDocs(ExecutableMemberDoc doc, List<String> errors) {
-        boolean result = true;
-        
-        ClassDoc exceptions[] = doc.thrownExceptions();
-        Set<ClassDoc> exceptionSet = new HashSet<ClassDoc>();
-        for (int i = 0; i < exceptions.length; i++) {
-            ClassDoc exception = exceptions[i];
-            if (isRelevantException(exception)) {
-                exceptionSet.add(exception);
-            }
-        }
-
-        ThrowsTag[] throwsTags = doc.throwsTags();
-        Map<ClassDoc, String> throwsTagsMap = new HashMap<ClassDoc, String>();
-        for (int i = 0; i < throwsTags.length; i++) {
-            ThrowsTag throwsTag = throwsTags[i];
-
-            if (throwsTag.exception() == null) {
-                errors.add("Unknown exception \"" + throwsTag.exceptionName() + "\"");
-                result = false;
-            } else if (isRelevantException(throwsTag.exception())) {
-
-                ClassDoc exception = throwsTag.exception();
-                while (exception != null && !exceptionSet.contains(exception)) {
-                    exception = exception.superclass();
-                }
-                if (exception == null) {
-                    errors.add("Unknown exception \"" + throwsTag.exceptionName() + "\"");
-                    result = false;
-                }
-            }
-            
-            throwsTagsMap.put(throwsTag.exception(), throwsTag.exceptionComment());
-        }
-
-        for (int i = 0; i < exceptions.length; i++) {
-            ClassDoc exception = exceptions[i];
-            boolean found = false;
-            
-            for (int j = 0; j < throwsTags.length && !found; j++) {
-                ThrowsTag throwsTag = throwsTags[j];
-                
-                ClassDoc candidate = throwsTag.exception();
-                if (candidate != null) {
-                    if (candidate.equals(exception) || candidate.subclassOf(exception)) {
-                        if (isValidComment(throwsTag.exceptionComment())) {
-                            found = true;
-                        }
-                    }
-                }
-            }
-            
-            if (!found) {
-                errors.add("Undocumented exception \"" + exception.name() + "\"");
-                result = false;
-            }
-        }
-
-        return result;
-    }
-
-    /**
-     * Checks whether an exception needs to be documented. Runtime exceptions
-     * and errors don't necessarily need documentation (although it doesn't
-     * hurt to have it).
-     */
-    private boolean isRelevantException(ClassDoc clazz) {
-        return !(clazz.subclassOf(runtimeException) || clazz.subclassOf(error));
-    }
-    
-    /**
-     * Checks whether the given method has documentation for the return value.
-     */
-    private boolean hasReturnDoc(MethodDoc method, List<String> errors) {
-        boolean result = true;
-        
-        if (!"void".equals(method.returnType().typeName())) {
-            Tag[] returnTags = method.tags("return");
-
-            if (returnTags.length == 0) {
-                errors.add("Missing result.");
-                result = false;
-            }
-
-            for (int i = 0; i < returnTags.length; i++) {
-                Tag tag = returnTags[i];
-                if (!isValidComment(tag.text())) {
-                    errors.add("Insufficient result.");
-                    result = false;
-                }
-            }
-        } else {
-            Tag[] returnTags = method.tags("return");
-            if (returnTags.length != 0) {
-                errors.add("Unknown result.");
-                result = false;
-            }
-        }
-
-        return result;
-    }
-
-    /**
-     * Returns the first sentence for the given documentation element.
-     */
-    private String getFirstSentence(Doc doc) {
-        StringBuilder builder = new StringBuilder();
-
-        Tag[] tags = doc.firstSentenceTags();
-        for (int i = 0; i < tags.length; i++) {
-            Tag tag = tags[i];
-
-            if ("Text".equals(tag.kind())) {
-                builder.append(tag.text());
-            } else {
-                builder.append("{" + tag.toString() + "}");
-            }
-        }
-
-        return builder.toString();
-    }
-
-    /**
-     * Returns the interface method that a given method implements, or null if
-     * the method does not implement any interface method.
-     */
-    private MethodDoc implementedMethod(MethodDoc doc) {
-        ClassDoc clazz = doc.containingClass();
-        MethodDoc myDoc = null;
-        while(clazz != null && myDoc == null){
-        ClassDoc[] interfaces = clazz.interfaces();
-            myDoc = implementedMethod0(doc, interfaces);
-            clazz = clazz.superclass();
-        }
-        return myDoc;
-    }
-
-    /**
-     * Recursive helper method for finding out which interface method a given
-     * method implements.
-     */
-    private MethodDoc implementedMethod0(MethodDoc doc, ClassDoc[] interfaces) {
-        for (int i = 0; i < interfaces.length; i++) {
-            ClassDoc classDoc = interfaces[i];
-
-            MethodDoc[] methods = classDoc.methods();
-            for (int j = 0; j < methods.length; j++) {
-                MethodDoc methodDoc = methods[j];
-                if (doc.overrides(methodDoc)) {
-                    return methodDoc;
-                }
-            }
-        }
-
-        for (int i = 0; i < interfaces.length; i++) {
-            MethodDoc myDoc = implementedMethod0(doc, interfaces[i].interfaces());
-            if (myDoc != null) {
-                return myDoc;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Checks whether the given documentation element has a "@since" tag for
-     * Android.
-     */
-    private boolean hasSinceTag(Doc doc) {
-        Tag[] tags = doc.tags("since");
-
-        for (int i = 0; i < tags.length; i++) {
-            if ("Android 1.0".equals(tags[i].text())) {
-                return true;
-            }
-        }
-
-        if (doc instanceof MemberDoc) {
-            return hasSinceTag(((MemberDoc)doc).containingClass());
-        }
-        
-        if (doc instanceof ClassDoc) {
-            return hasSinceTag(((ClassDoc)doc).containingPackage());
-        }
-        
-        return false;
-    }
-
-    /**
-     * Checks whether the given documentation element has a "@hide" tag that
-     * excludes it from the official API.
-     */
-    private boolean isHidden(Doc doc) {
-        Tag[] tags = doc.tags("hide");
-
-        return tags != null && tags.length != 0;
-    }
-    
-    /**
-     * Determines the status of an element based on the existence of
-     * documentation, the review status, and any comments it might have.
-     */
-    private int getStatus(boolean documented, boolean reviewed, Tag[] comments) {
-        if (!documented) {
-            return VALUE_RED;
-        } else if (reviewed && comments.length == 0) {
-            return VALUE_GREEN;
-        } else {
-            return VALUE_YELLOW;
-        }
-    }
-
-    /**
-     * Called by JavaDoc to find our which command line arguments are supported
-     * and how many parameters they take. Part of the JavaDoc API.
-     */
-    public static int optionLength(String option) {
-        if ("-d".equals(option)) {
-            return 2;
-        } else if("-Xgeneric".equals(option)){
-            return 1;
-        } else {
-            return 0;
-        }
-    }
-
-    /**
-     * Returns a particular command line argument for a given option.
-     */
-    private static String getOption(RootDoc root, String option, int index, String defValue) {
-        String[][] allOptions = root.options();
-        for (int i = 0; i < allOptions.length; i++) {
-            if (allOptions[i][0].equals(option)) {
-                return allOptions[i][index];
-            }
-        }
-
-        return defValue;
-    }
-    
-    /**
-     * Returns whether the specified option is present.
-     */
-    private static boolean isOptionSet(RootDoc root, String option){
-        String[][] allOptions = root.options();
-        for (int i = 0; i < allOptions.length; i++) {
-            if (allOptions[i][0].equals(option)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Called by JavaDoc to find out which Java version we claim to support.
-     * Part of the JavaDoc API.
-     */
-    public static LanguageVersion languageVersion() {
-        return LanguageVersion.JAVA_1_5;
-    }
-
-    /**
-     * The main entry point called by JavaDoc after all required information has
-     * been collected. Part of the JavaDoc API.
-     */
-    public static boolean start(RootDoc root) {
-        try {
-            String target = getOption(root, "-d", 1, ".");
-            checkTypeParameters = isOptionSet(root, "-Xgeneric");
-
-            SpecProgressDoclet doclet = new SpecProgressDoclet(target);
-            doclet.process(root);
-
-            File file = new File(target, "index.html");
-            System.out.println("Please see complete report in " + 
-                    file.getAbsolutePath());
-            
-        } catch (Exception ex) {
-            ex.printStackTrace();
-            return false;
-        }
-
-        return true;
-    }
-
-}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildHelper.java b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildHelper.java
index 4a2d56c..9bc0945 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildHelper.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildHelper.java
@@ -16,6 +16,7 @@
 
 package com.android.cts.tradefed.build;
 
+import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.build.IFolderBuildInfo;
 
 import java.io.File;
@@ -57,6 +58,29 @@
     }
 
     /**
+     * A helper factory method that creates and validates a {@link CtsBuildHelper} given an
+     * {@link IBuildInfo}.
+     *
+     * @param build the {@link IBuildInfo}
+     * @return the {@link CtsBuildHelper}
+     * @throws IllegalArgumentException if provided <var>build</var> is not a valid CTS build
+     */
+    public static CtsBuildHelper createBuildHelper(IBuildInfo build) {
+        if (!(build instanceof IFolderBuildInfo)) {
+            throw new IllegalArgumentException(String.format(
+                    "Wrong build type. Expected %s, received %s", IFolderBuildInfo.class.getName(),
+                    build.getClass().getName()));
+        }
+        try {
+            CtsBuildHelper ctsBuild = new CtsBuildHelper((IFolderBuildInfo)build);
+            ctsBuild.validateStructure();
+            return ctsBuild;
+        } catch (FileNotFoundException e) {
+            throw new IllegalArgumentException("Invalid CTS build provided.", e);
+        }
+    }
+
+    /**
      * @return a {@link File} representing the parent folder of the CTS installation
      */
     public File getRootDir() {
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java
index 2834793..f6d8142 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java
@@ -22,7 +22,6 @@
 import com.android.ddmlib.Log.LogLevel;
 import com.android.ddmlib.testrunner.TestIdentifier;
 import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.build.IFolderBuildInfo;
 import com.android.tradefed.config.Option;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
@@ -32,7 +31,6 @@
 import com.android.tradefed.testtype.IRemoteTest;
 import com.android.tradefed.testtype.IResumableTest;
 import com.android.tradefed.testtype.IShardableTest;
-import com.android.tradefed.testtype.InstrumentationTest;
 import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
 
 import java.io.BufferedInputStream;
@@ -119,6 +117,7 @@
     private List<KnownTests> mRemainingTests = null;
 
     private CtsBuildHelper mCtsBuild = null;
+    private IBuildInfo mBuildInfo = null;
 
     /**
      * {@inheritDoc}
@@ -201,17 +200,8 @@
      */
     @Override
     public void setBuild(IBuildInfo build) {
-        if (!(build instanceof IFolderBuildInfo)) {
-            throw new IllegalArgumentException(String.format(
-                    "Wrong build type. Expected %s, received %s", IFolderBuildInfo.class.getName(),
-                    build.getClass().getName()));
-        }
-        try {
-            mCtsBuild = new CtsBuildHelper((IFolderBuildInfo)build);
-            mCtsBuild.validateStructure();
-        } catch (FileNotFoundException e) {
-            throw new IllegalArgumentException("Invalid CTS build provided.", e);
-        }
+        mCtsBuild = CtsBuildHelper.createBuildHelper(build);
+        mBuildInfo = build;
     }
 
     /**
@@ -249,12 +239,10 @@
             if (test instanceof IDeviceTest) {
                 ((IDeviceTest)test).setDevice(getDevice());
             }
-            // Increment the timeout for collecting the tests.
-            // TODO: move this to testPackage.createTest() instead and only increase timeout when
-            // tests number is large.
-            if (test instanceof InstrumentationTest) {
-                ((InstrumentationTest)test).setCollectsTestsShellTimeout(10*60*1000);
+            if (test instanceof IBuildReceiver) {
+                ((IBuildReceiver)test).setBuild(mBuildInfo);
             }
+
             ResultFilter filter = new ResultFilter(listener, testPair.getKnownTests());
             test.run(filter);
             mRemainingTests.remove(0);
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPackageDef.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPackageDef.java
index e4f13b5..f4258d1 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPackageDef.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPackageDef.java
@@ -27,7 +27,7 @@
  * <p/>
  * Knows how to translate this info into a runnable {@link IRemoteTest}.
  */
-interface ITestPackageDef {
+public interface ITestPackageDef {
 
     /**
      * Get the unique URI of the test package.
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/InstrumentationApkTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/InstrumentationApkTest.java
new file mode 100644
index 0000000..e1bb91e
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/InstrumentationApkTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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 com.android.cts.tradefed.testtype;
+
+import com.android.cts.tradefed.build.CtsBuildHelper;
+import com.android.ddmlib.Log;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.testtype.InstrumentationTest;
+
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import junit.framework.Assert;
+
+/**
+ * A {@link InstrumentationTest] that will install CTS apks before test execution,
+ * and uninstall on execution completion.
+ */
+public class InstrumentationApkTest extends InstrumentationTest implements IBuildReceiver {
+
+    private static final String LOG_TAG = "InstrumentationApkTest";
+
+    /** the file names of the CTS apks to install */
+    private Collection<String> mInstallFileNames = new ArrayList<String>();
+    private Collection<String> mUninstallPackages = new ArrayList<String>();
+
+    private CtsBuildHelper mCtsBuild = null;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setBuild(IBuildInfo build) {
+        mCtsBuild  = CtsBuildHelper.createBuildHelper(build);
+    }
+
+    /**
+     * Add an apk to install.
+     *
+     * @param apkFileName the apk file name
+     * @param packageName the apk's Android package name
+     */
+    public void addInstallApk(String apkFileName, String packageName) {
+        mInstallFileNames.add(apkFileName);
+        mUninstallPackages.add(packageName);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void run(final ITestInvocationListener listener)
+            throws DeviceNotAvailableException {
+        Assert.assertNotNull("missing device", getDevice());
+        Assert.assertNotNull("missing build", mCtsBuild);
+
+        try {
+            for (String apkFileName : mInstallFileNames) {
+                Log.d(LOG_TAG, String.format("Installing %s on %s", apkFileName,
+                        getDevice().getSerialNumber()));
+                try {
+                    getDevice().installPackage(mCtsBuild.getTestApp(apkFileName), true);
+                } catch (FileNotFoundException e) {
+                    Log.e(LOG_TAG, e);
+                }
+            }
+            super.run(listener);
+        } finally {
+            for (String packageName : mUninstallPackages) {
+                Log.d(LOG_TAG, String.format("Uninstalling %s on %s", packageName,
+                        getDevice().getSerialNumber()));
+                getDevice().uninstallPackage(packageName);
+            }
+        }
+    }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/InstrumentationAppTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/InstrumentationAppTest.java
deleted file mode 100644
index 47fc14a..0000000
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/InstrumentationAppTest.java
+++ /dev/null
@@ -1,72 +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 com.android.cts.tradefed.testtype;
-
-import com.android.ddmlib.Log;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.result.ITestInvocationListener;
-import com.android.tradefed.testtype.InstrumentationTest;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Collection;
-
-/**
- * A {@link InstrumentationTest] that will install other dependent apks before test execution,
- * and uninstall apps on execution completion.
- */
-public class InstrumentationAppTest extends InstrumentationTest {
-
-    private static final String LOG_TAG = "InstrumentationAppTest";
-
-    // TODO: consider moving this class to tradefed proper
-
-    private Collection<File> mInstallFiles = new ArrayList<File>();
-    private Collection<String> mInstallPackages = new ArrayList<String>();
-
-    /**
-     * Add a dependent apk to install.
-     *
-     * @param apkFile the apk file
-     * @param packageName the apk's Android package name
-     */
-    public void addInstallApp(File apkFile, String packageName) {
-        mInstallFiles.add(apkFile);
-        mInstallPackages.add(packageName);
-    }
-
-    @Override
-    public void run(final ITestInvocationListener listener)
-            throws DeviceNotAvailableException {
-        if (getDevice() == null) {
-            throw new IllegalStateException("missing device");
-        }
-        try {
-            for (File apkFile : mInstallFiles) {
-                Log.d(LOG_TAG, String.format("Installing %s on %s", apkFile.getName(),
-                        getDevice().getSerialNumber()));
-                getDevice().installPackage(apkFile, true);
-            }
-            super.run(listener);
-        } finally {
-            for (String packageName : mInstallPackages) {
-                Log.d(LOG_TAG, String.format("Uninstalling %s on %s", packageName,
-                        getDevice().getSerialNumber()));
-                getDevice().uninstallPackage(packageName);
-            }
-        }
-    }
-}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/JarHostTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/JarHostTest.java
index 041079d..2fe6802 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/JarHostTest.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/JarHostTest.java
@@ -15,12 +15,15 @@
  */
 package com.android.cts.tradefed.testtype;
 
+import com.android.cts.tradefed.build.CtsBuildHelper;
 import com.android.ddmlib.Log;
 import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.result.ITestInvocationListener;
 import com.android.tradefed.result.JUnitToInvocationResultForwarder;
+import com.android.tradefed.testtype.IBuildReceiver;
 import com.android.tradefed.testtype.IDeviceTest;
 import com.android.tradefed.testtype.IRemoteTest;
 import com.android.tradefed.util.CommandStatus;
@@ -28,6 +31,7 @@
 import com.android.tradefed.util.RunUtil;
 
 import java.io.File;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.net.URL;
 import java.net.URLClassLoader;
@@ -39,29 +43,50 @@
 import junit.framework.TestResult;
 
 /**
- * A {@link IRemoteTest} that can run a set of JUnit tests from a jar.
+ * A {@link IRemoteTest} that can run a set of JUnit tests from a CTS jar.
  */
-public class JarHostTest implements IDeviceTest, IRemoteTest {
+public class JarHostTest implements IDeviceTest, IRemoteTest, IBuildReceiver {
 
     private static final String LOG_TAG = "JarHostTest";
 
     private ITestDevice mDevice;
-    private File mJarFile;
+    private String mJarFileName;
     private Collection<TestIdentifier> mTests;
     private long mTimeoutMs = 10 * 60 * 1000;
     private String mRunName;
-    private String mTestAppPath;
+    private CtsBuildHelper mCtsBuild = null;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setBuild(IBuildInfo buildInfo) {
+        mCtsBuild = CtsBuildHelper.createBuildHelper(buildInfo);
+    }
+
+    /**
+     * Set the CTS build container.
+     * <p/>
+     * Exposed so unit tests can mock the provided build.
+     *
+     * @param buildHelper
+     */
+    void setBuildHelper(CtsBuildHelper buildHelper) {
+        mCtsBuild = buildHelper;
+    }
 
     /**
      * Set the jar file to load tests from.
-     * @param jarFile
+     *
+     * @param jarFileName the file name of the CTS host test jar to use
      */
-    void setJarFile(File jarFile) {
-        mJarFile = jarFile;
+    void setJarFileName(String jarFileName) {
+        mJarFileName = jarFileName;
     }
 
     /**
      * Sets the collection of tests to run
+     *
      * @param tests
      */
     void setTests(Collection<TestIdentifier> tests) {
@@ -90,17 +115,6 @@
     }
 
     /**
-     * Set the filesystem path to test app artifacts needed to run tests.
-     *
-     * @see {@link com.android.hosttest.DeviceTest#setTestAppPath(String)}
-     *
-     * @param testAppPath
-     */
-    void setTestAppPath(String testAppPath) {
-        mTestAppPath = testAppPath;
-    }
-
-    /**
      * {@inheritDoc}
      */
     @Override
@@ -153,7 +167,7 @@
             // all host tests are converted to use tradefed
             com.android.hosttest.DeviceTest deviceTest = (com.android.hosttest.DeviceTest)junitTest;
             deviceTest.setDevice(getDevice().getIDevice());
-            deviceTest.setTestAppPath(mTestAppPath);
+            deviceTest.setTestAppPath(mCtsBuild.getTestCasesDir().getAbsolutePath());
         }
         CommandStatus status = RunUtil.getInstance().runTimed(mTimeoutMs, new IRunnableResult() {
 
@@ -183,7 +197,8 @@
      */
     private Test loadTest(String className, String testName) {
         try {
-            URL urls[] = {mJarFile.getCanonicalFile().toURI().toURL()};
+            File jarFile = mCtsBuild.getTestApp(mJarFileName);
+            URL urls[] = {jarFile.getCanonicalFile().toURI().toURL()};
             Class<?> testClass = loadClass(className, urls);
 
             if (TestCase.class.isAssignableFrom(testClass)) {
@@ -195,16 +210,16 @@
                 return test;
             } else {
                 Log.e(LOG_TAG, String.format("Class '%s' from jar '%s' is not a Test",
-                        className, mJarFile.getAbsolutePath()));
+                        className, mJarFileName));
             }
         } catch (ClassNotFoundException e) {
-            reportLoadError(mJarFile, className, e);
+            reportLoadError(mJarFileName, className, e);
         } catch (IllegalAccessException e) {
-            reportLoadError(mJarFile, className, e);
+            reportLoadError(mJarFileName, className, e);
         } catch (IOException e) {
-            reportLoadError(mJarFile, className, e);
+            reportLoadError(mJarFileName, className, e);
         } catch (InstantiationException e) {
-            reportLoadError(mJarFile, className, e);
+            reportLoadError(mJarFileName, className, e);
         }
         return null;
     }
@@ -225,9 +240,9 @@
         return testClass;
     }
 
-    private void reportLoadError(File jarFile, String className, Exception e) {
+    private void reportLoadError(String jarFileName, String className, Exception e) {
         Log.e(LOG_TAG, String.format("Failed to load test class '%s' from jar '%s'",
-                className, jarFile.getAbsolutePath()));
+                className, jarFileName));
         Log.e(LOG_TAG, e);
     }
 
@@ -241,11 +256,21 @@
         if (mDevice == null) {
             throw new IllegalArgumentException("Device has not been set");
         }
-        if (mJarFile == null) {
-            throw new IllegalArgumentException("jar file has not been set");
+        if (mJarFileName == null) {
+            throw new IllegalArgumentException("jar file name has not been set");
         }
         if (mTests == null) {
             throw new IllegalArgumentException("tests has not been set");
         }
+        if (mCtsBuild == null) {
+            throw new IllegalArgumentException("build has not been set");
+        }
+        try {
+            mCtsBuild.getTestApp(mJarFileName);
+        } catch (FileNotFoundException e) {
+            throw new IllegalArgumentException(String.format(
+                    "Could not find jar %s in CTS build %s", mJarFileName,
+                    mCtsBuild.getRootDir().getAbsolutePath()));
+        }
     }
 }
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java
index fd896f2..04da318 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java
@@ -136,8 +136,7 @@
             Log.d(LOG_TAG, String.format("Creating host test for %s", mName));
             JarHostTest hostTest = new JarHostTest();
             hostTest.setRunName(mName);
-            hostTest.setJarFile(new File(testCaseDir, mJarPath));
-            hostTest.setTestAppPath(testCaseDir.getAbsolutePath());
+            hostTest.setJarFileName(mJarPath);
             hostTest.setTests(filterTests(mTests, className, methodName));
             return hostTest;
         } else if (mIsSignatureTest) {
@@ -145,7 +144,7 @@
             // points to specialized instrumentation. Eventually this special case for signatureTest
             // can be removed, and it can be treated just like a normal InstrumentationTest
             Log.d(LOG_TAG, String.format("Creating signature test %s", mName));
-            InstrumentationTest instrTest = new InstrumentationTest();
+            InstrumentationApkTest instrTest = new InstrumentationApkTest();
             instrTest.setPackageName(mAppNameSpace);
             instrTest.setRunnerName("android.test.InstrumentationTestRunner");
             instrTest.setClassName(SIGNATURE_TEST_CLASS);
@@ -153,34 +152,22 @@
             // add signature test to list of known tests
             addTest(new TestIdentifier(SIGNATURE_TEST_CLASS, SIGNATURE_TEST_METHOD));
             // mName means 'apk file name' for instrumentation tests
-            File apkFile = new File(testCaseDir, String.format("%s.apk", mName));
-            if (!apkFile.exists()) {
-                Log.w(LOG_TAG, String.format("Could not find apk file %s",
-                        apkFile.getAbsolutePath()));
-                return null;
-            }
-            instrTest.setInstallFile(apkFile);
+            instrTest.addInstallApk(String.format("%s.apk", mName), mAppNameSpace);
             return instrTest;
         } else if (mIsReferenceAppTest) {
             // a reference app test is just a InstrumentationTest with one extra apk to install
-            InstrumentationAppTest instrTest = new InstrumentationAppTest();
-            File apkFile = new File(testCaseDir, String.format("%s.apk", mApkToTestName));
-            if (!apkFile.exists()) {
-                Log.w(LOG_TAG, String.format("Could not find apk file %s",
-                        apkFile.getAbsolutePath()));
-                return null;
-            }
-            instrTest.addInstallApp(apkFile, mPackageToTest);
-            return setInstrumentationTest(testCaseDir, className, methodName, instrTest);
+            InstrumentationApkTest instrTest = new InstrumentationApkTest();
+            instrTest.addInstallApk(String.format("%s.apk", mApkToTestName), mPackageToTest);
+            return setInstrumentationTest(className, methodName, instrTest);
         } else {
             Log.d(LOG_TAG, String.format("Creating instrumentation test for %s", mName));
-            InstrumentationTest instrTest = new InstrumentationTest();
-            return setInstrumentationTest(testCaseDir, className, methodName, instrTest);
+            InstrumentationApkTest instrTest = new InstrumentationApkTest();
+            return setInstrumentationTest(className, methodName, instrTest);
         }
     }
 
     /**
-     * Populates given {@link InstrumentationTest} with data from the package xml
+     * Populates given {@link InstrumentationApkTest} with data from the package xml
      *
      * @param testCaseDir
      * @param className
@@ -188,20 +175,18 @@
      * @param instrTest
      * @return the populated {@link InstrumentationTest} or <code>null</code>
      */
-    private InstrumentationTest setInstrumentationTest(File testCaseDir, String className,
-            String methodName, InstrumentationTest instrTest) {
+    private InstrumentationApkTest setInstrumentationTest(String className,
+            String methodName, InstrumentationApkTest instrTest) {
         instrTest.setPackageName(mAppNameSpace);
         instrTest.setRunnerName(mRunner);
         instrTest.setClassName(className);
         instrTest.setMethodName(methodName);
         // mName means 'apk file name' for instrumentation tests
-        File apkFile = new File(testCaseDir, String.format("%s.apk", mName));
-        if (!apkFile.exists()) {
-            Log.w(LOG_TAG, String.format("Could not find apk file %s",
-                    apkFile.getAbsolutePath()));
-            return null;
+        instrTest.addInstallApk(String.format("%s.apk", mName), mAppNameSpace);
+        if (mTests.size() > 1000) {
+            // TODO: hack, large test suites can take longer to collect tests, increase timeout
+            instrTest.setCollectsTestsShellTimeout(10*60*1000);
         }
-        instrTest.setInstallFile(apkFile);
         return instrTest;
     }
 
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/JarHostTestTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/JarHostTestTest.java
index 9a49b77..dfe4413 100644
--- a/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/JarHostTestTest.java
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/JarHostTestTest.java
@@ -15,6 +15,7 @@
  */
 package com.android.cts.tradefed.testtype;
 
+import com.android.cts.tradefed.build.StubCtsBuildHelper;
 import com.android.ddmlib.testrunner.TestIdentifier;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
@@ -22,7 +23,6 @@
 
 import org.easymock.EasyMock;
 
-import java.io.File;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -37,6 +37,8 @@
 
     private static final String RUN_NAME = "run";
     private JarHostTest mJarTest;
+    private StubCtsBuildHelper mStubBuildHelper;
+
 
     @Override
     protected void setUp() throws Exception {
@@ -48,6 +50,8 @@
                 return MockTest.class;
             }
         };
+        mStubBuildHelper = new StubCtsBuildHelper();
+        mJarTest.setBuildHelper(mStubBuildHelper);
     }
 
     public static class MockTest extends TestCase {
@@ -80,7 +84,7 @@
         listener.testRunEnded(EasyMock.anyLong(), EasyMock.eq(Collections.EMPTY_MAP));
         mJarTest.setTests(tests);
         mJarTest.setDevice(EasyMock.createMock(ITestDevice.class));
-        mJarTest.setJarFile(new File("fakefile"));
+        mJarTest.setJarFileName("fakefile");
         mJarTest.setRunName(RUN_NAME);
 
         EasyMock.replay(listener);
diff --git a/tools/utils/buildCts.py b/tools/utils/buildCts.py
index 7d4b6e0..4daf48e 100755
--- a/tools/utils/buildCts.py
+++ b/tools/utils/buildCts.py
@@ -23,7 +23,7 @@
 import sys
 import xml.dom.minidom as dom
 from cts import tools
-
+from multiprocessing import Pool
 
 def GetSubDirectories(root):
   """Return all directories under the given root directory."""
@@ -72,174 +72,24 @@
     self.test_repository = os.path.join(self.out_dir, 'repository/testcases')
     self.plan_repository = os.path.join(self.out_dir, 'repository/plans')
 
-  def __LogGenerateDescription(self, name):
-    print 'Generating test description for package %s' % name
-
-  def RunDescriptionGeneratorDoclet(self, source_root, output_file):
-    """Generate a test package description by running the DescriptionGenerator doclet.
-
-    Args:
-      source_root: Directory under which tests should be searched.
-      output_file: Name of the file where the description gets written.
-
-    Returns:
-      The exit code of the DescriptionGenerator doclet run.
-    """
-    # Make sure sourceRoot is relative to  self.android_root
-    source_root = self.RelPath(source_root, self.android_root)
-
-    # To determine whether a class is a JUnit test, the Doclet needs to have all intermediate
-    # subclasses of TestCase as well as the JUnit framework itself on the source path.
-    # Annotation classes are also required, since test annotations go into the description.
-    source_path = [
-        'frameworks/base/core/java',            # android test classes
-        'frameworks/base/test-runner/src',      # test runner
-        'libcore/junit/src/main/java',          # junit classes
-        'development/tools/hosttestlib/src',    # hosttestlib TestCase extensions
-        'libcore/dalvik/src/main/java',         # test annotations
-        'cts/libs/annotation/src',              # cts annotations
-        'cts/tests/src',                        # cts test stubs
-        source_root                             # the source for this package
-    ]
-    source_path = [os.path.join(self.android_root, x) for x in source_path]
-    cmd = ('javadoc -o %s -J-Xmx512m -quiet -doclet DescriptionGenerator -docletpath %s'
-           ' -sourcepath %s ') % (output_file, self.doclet_path, ':'.join(source_path))
-    sources = []
-
-    def AddFile(sources, folder, names):
-      """Find *.java."""
-      sources.extend([os.path.join(folder, name) for name in names if name.endswith('.java')])
-
-    os.path.walk(os.path.join(self.android_root, source_root), AddFile, sources)
-    cmd += ' '.join(sources)
-    proc = subprocess.Popen(cmd, shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
-    # read and discard any output
-    proc.communicate()
-    # wait for process to terminate and return exit value
-    return proc.wait()
-
-  def GenerateSignatureCheckDescription(self):
-    """Generate the test description for the signature check."""
-    self.__LogGenerateDescription('android.tests.sigtest')
-    package = tools.TestPackage('SignatureTest', 'android.tests.sigtest')
-    package.AddAttribute('appNameSpace', 'android.tests.sigtest')
-    package.AddAttribute('signatureCheck', 'true')
-    package.AddAttribute('runner', '.InstrumentationRunner')
-    package.AddTest('android.tests.sigtest.SignatureTest.signatureTest')
-    description = open(os.path.join(self.test_repository, 'SignatureTest.xml'), 'w')
-    package.WriteDescription(description)
-    description.close()
-
-  def GenerateReferenceAppDescription(self):
-    """Generate the test description for the reference app tests."""
-    self.__LogGenerateDescription('android.apidemos.cts')
-    package = tools.TestPackage('ApiDemosReferenceTest', 'android.apidemos.cts')
-    package.AddAttribute('appNameSpace', 'android.apidemos.cts')
-    package.AddAttribute('packageToTest', 'com.example.android.apis')
-    package.AddAttribute('apkToTestName', 'ApiDemos')
-    package.AddAttribute('runner', 'android.test.InstrumentationTestRunner')
-    package.AddAttribute('referenceAppTest', 'true')
-    package.AddTest('android.apidemos.cts.ApiDemosTest.testNumberOfItemsInListView')
-    description = open(os.path.join(self.test_repository, 'ApiDemosReferenceTest.xml'), 'w')
-    package.WriteDescription(description)
-    description.close()
-
-  def GenerateAppSecurityDescription(self):
-    """Generate the test description for the application security tests."""
-    test_root = 'cts/tests/appsecurity-tests'
-    makefile_name = os.path.join(test_root, 'Android.mk')
-    makefile_vars = GetMakeFileVars(makefile_name)
-    name = makefile_vars['LOCAL_MODULE']
-    package_name = 'android.tests.appsecurity'
-    self.__LogGenerateDescription(package_name)
-    temp_desc = os.path.join(self.temp_dir, 'description.xml')
-    self.RunDescriptionGeneratorDoclet(os.path.join(test_root, 'src'), temp_desc)
-    doc = dom.parse(temp_desc)
-    test_description = doc.getElementsByTagName('TestPackage')[0]
-    test_description.setAttribute('name', package_name)
-    test_description.setAttribute('appPackageName', package_name)
-    test_description.setAttribute('hostSideOnly', 'true')
-    test_description.setAttribute('jarPath', name + '.jar')
-    description = open(os.path.join(self.test_repository, package_name + '.xml'), 'w')
-    doc.writexml(description, addindent='    ', encoding='UTF-8')
-    description.close()
-
-  @staticmethod
-  def RelPath(path, start=os.getcwd()):
-    """Get a relative version of a path.
-
-    This is equivalent to os.path.relpath, which is only available since Python 2.6.
-
-    Args:
-      path: The path to transform.
-      start: The base path. Defaults to the current working directory.
-
-    Returns:
-      A transformed path that is relative to start.
-    """
-    path_dirs = os.path.abspath(path).split(os.path.sep)
-    start_dirs = os.path.abspath(start).split(os.path.sep)
-
-    num_common = len(os.path.commonprefix([start_dirs, path_dirs]))
-
-    result_dirs = ['..'] * (len(start_dirs) - num_common) + path_dirs[num_common:]
-    if result_dirs:
-      return os.path.join(*result_dirs)
-    return start
-
   def GenerateTestDescriptions(self):
     """Generate test descriptions for all packages."""
+    pool = Pool(processes=16)
+
     # individually generate descriptions not following conventions
-    self.GenerateSignatureCheckDescription()
-    self.GenerateReferenceAppDescription()
-    self.GenerateAppSecurityDescription()
+    pool.apply_async(GenerateSignatureCheckDescription, [self.test_repository])
+    pool.apply_async(GenerateReferenceAppDescription, [self.test_repository])
+    pool.apply_async(GenerateAppSecurityDescription, [self.temp_dir,
+        self.test_repository, self.android_root, self.doclet_path])
 
     # generate test descriptions for android tests
     android_packages = GetSubDirectories(self.test_root)
     for package in android_packages:
-      app_package_name = 'android.' + package
-      package_root = os.path.join(self.test_root, package)
+      pool.apply_async(GenerateTestDescription, [self.test_root, self.temp_dir,
+          self.test_repository, self.android_root, self.doclet_path, package])
 
-      makefile_name = os.path.join(package_root, 'Android.mk')
-      if not os.path.exists(makefile_name):
-        print 'Skipping directory "%s" due to missing Android.mk' % package_root
-        continue
-      makefile_vars = GetMakeFileVars(makefile_name)
-
-      manifest_name = os.path.join(package_root, 'AndroidManifest.xml')
-      if not os.path.exists(manifest_name):
-        print 'Skipping directory "%s" due to missing AndroidManifest.xml' % package_root
-        continue
-      manifest = tools.XmlFile(manifest_name)
-
-      self.__LogGenerateDescription(app_package_name)
-
-      # Run the description generator doclet to get the test package structure
-      # TODO: The Doclet does not currently add all required attributes. Instead of rewriting
-      # the document below, additional attributes should be passed to the Doclet as arguments.
-      temp_desc = os.path.join(self.temp_dir, 'description.xml')
-      self.RunDescriptionGeneratorDoclet(package_root, temp_desc)
-
-      # obtain missing attribute values from the makefile and manifest
-      package_name = makefile_vars['LOCAL_PACKAGE_NAME']
-      runner = manifest.GetAndroidAttr('instrumentation', 'name')
-      target_package = manifest.GetAndroidAttr('instrumentation', 'targetPackage')
-      target_binary_name = makefile_vars.get('LOCAL_INSTRUMENTATION_FOR')
-
-      # add them to the document
-      doc = dom.parse(temp_desc)
-      test_description = doc.getElementsByTagName('TestPackage')[0]
-      test_description.setAttribute('name', package_name)
-      test_description.setAttribute('runner', runner)
-      test_package = manifest.GetAttr('manifest', 'package')
-      test_description.setAttribute('appNameSpace', test_package)
-      test_description.setAttribute('appPackageName', app_package_name)
-      if not test_package == target_package:
-        test_description.setAttribute('targetNameSpace', target_package)
-        test_description.setAttribute('targetBinaryName', target_binary_name)
-      description = open(os.path.join(self.test_repository, package_name + '.xml'), 'w')
-      doc.writexml(description, addindent='    ', encoding='UTF-8')
-      description.close()
+    pool.close()
+    pool.join()
 
   def __WritePlan(self, plan, plan_name):
     print 'Generating test plan %s' % plan_name
@@ -286,6 +136,170 @@
     plan.Include(r'android\.tests\.appsecurity')
     self.__WritePlan(plan, 'AppSecurity')
 
+def LogGenerateDescription(name):
+  print 'Generating test description for package %s' % name
+
+def GenerateSignatureCheckDescription(test_repository):
+  """Generate the test description for the signature check."""
+  LogGenerateDescription('android.tests.sigtest')
+  package = tools.TestPackage('SignatureTest', 'android.tests.sigtest')
+  package.AddAttribute('appNameSpace', 'android.tests.sigtest')
+  package.AddAttribute('signatureCheck', 'true')
+  package.AddAttribute('runner', '.InstrumentationRunner')
+  package.AddTest('android.tests.sigtest.SignatureTest.signatureTest')
+  description = open(os.path.join(test_repository, 'SignatureTest.xml'), 'w')
+  package.WriteDescription(description)
+  description.close()
+
+def GenerateReferenceAppDescription(test_repository):
+  """Generate the test description for the reference app tests."""
+  LogGenerateDescription('android.apidemos.cts')
+  package = tools.TestPackage('ApiDemosReferenceTest', 'android.apidemos.cts')
+  package.AddAttribute('appNameSpace', 'android.apidemos.cts')
+  package.AddAttribute('packageToTest', 'com.example.android.apis')
+  package.AddAttribute('apkToTestName', 'ApiDemos')
+  package.AddAttribute('runner', 'android.test.InstrumentationTestRunner')
+  package.AddAttribute('referenceAppTest', 'true')
+  package.AddTest('android.apidemos.cts.ApiDemosTest.testNumberOfItemsInListView')
+  description = open(os.path.join(test_repository, 'ApiDemosReferenceTest.xml'), 'w')
+  package.WriteDescription(description)
+  description.close()
+
+def GenerateAppSecurityDescription(temp_dir, test_repository, android_root, doclet_path):
+  """Generate the test description for the application security tests."""
+  test_root = 'cts/tests/appsecurity-tests'
+  makefile_name = os.path.join(test_root, 'Android.mk')
+  makefile_vars = GetMakeFileVars(makefile_name)
+  name = makefile_vars['LOCAL_MODULE']
+  package_name = 'android.tests.appsecurity'
+  LogGenerateDescription(package_name)
+  temp_desc = os.path.join(temp_dir, 'description.xml')
+  RunDescriptionGeneratorDoclet(android_root, doclet_path,
+      os.path.join(test_root, 'src'), temp_desc)
+  doc = dom.parse(temp_desc)
+  test_description = doc.getElementsByTagName('TestPackage')[0]
+  test_description.setAttribute('name', package_name)
+  test_description.setAttribute('appPackageName', package_name)
+  test_description.setAttribute('hostSideOnly', 'true')
+  test_description.setAttribute('jarPath', name + '.jar')
+  description = open(os.path.join(test_repository, package_name + '.xml'), 'w')
+  doc.writexml(description, addindent='    ', encoding='UTF-8')
+  description.close()
+
+
+def GenerateTestDescription(test_root, temp_dir, test_repository, android_root,
+                            doclet_path, package):
+
+  app_package_name = 'android.' + package
+  package_root = os.path.join(test_root, package)
+
+  makefile_name = os.path.join(package_root, 'Android.mk')
+  if not os.path.exists(makefile_name):
+    print 'Skipping directory "%s" due to missing Android.mk' % package_root
+    return
+  makefile_vars = GetMakeFileVars(makefile_name)
+
+  manifest_name = os.path.join(package_root, 'AndroidManifest.xml')
+  if not os.path.exists(manifest_name):
+    print 'Skipping directory "%s" due to missing AndroidManifest.xml' % package_root
+    return
+  manifest = tools.XmlFile(manifest_name)
+
+  LogGenerateDescription(app_package_name)
+
+  # Run the description generator doclet to get the test package structure
+  # TODO: The Doclet does not currently add all required attributes. Instead of rewriting
+  # the document below, additional attributes should be passed to the Doclet as arguments.
+  temp_desc = os.path.join(temp_dir, app_package_name + '-description.xml')
+
+  RunDescriptionGeneratorDoclet(android_root, doclet_path, package_root, temp_desc)
+
+  # obtain missing attribute values from the makefile and manifest
+  package_name = makefile_vars['LOCAL_PACKAGE_NAME']
+  runner = manifest.GetAndroidAttr('instrumentation', 'name')
+  target_package = manifest.GetAndroidAttr('instrumentation', 'targetPackage')
+  target_binary_name = makefile_vars.get('LOCAL_INSTRUMENTATION_FOR')
+
+  # add them to the document
+  doc = dom.parse(temp_desc)
+  test_description = doc.getElementsByTagName('TestPackage')[0]
+  test_description.setAttribute('name', package_name)
+  test_description.setAttribute('runner', runner)
+  test_package = manifest.GetAttr('manifest', 'package')
+  test_description.setAttribute('appNameSpace', test_package)
+  test_description.setAttribute('appPackageName', app_package_name)
+  if not test_package == target_package:
+    test_description.setAttribute('targetNameSpace', target_package)
+    test_description.setAttribute('targetBinaryName', target_binary_name)
+  description = open(os.path.join(test_repository, package_name + '.xml'), 'w')
+  doc.writexml(description, addindent='    ', encoding='UTF-8')
+  description.close()
+
+def RunDescriptionGeneratorDoclet(android_root, doclet_path, source_root, output_file):
+  """Generate a test package description by running the DescriptionGenerator doclet.
+
+  Args:
+    android_root: Root directory of the Android source tree.
+    doclet_path: Class path where the DescriptionGenerator doclet can be found.
+    source_root: Directory under which tests should be searched.
+    output_file: Name of the file where the description gets written.
+
+  Returns:
+    The exit code of the DescriptionGenerator doclet run.
+  """
+  # Make sure sourceRoot is relative to  self.android_root
+  source_root = RelPath(source_root, android_root)
+
+  # To determine whether a class is a JUnit test, the Doclet needs to have all intermediate
+  # subclasses of TestCase as well as the JUnit framework itself on the source path.
+  # Annotation classes are also required, since test annotations go into the description.
+  source_path = [
+      'frameworks/base/core/java',            # android test classes
+      'frameworks/base/test-runner/src',      # test runner
+      'libcore/junit/src/main/java',          # junit classes
+      'development/tools/hosttestlib/src',    # hosttestlib TestCase extensions
+      'libcore/dalvik/src/main/java',         # test annotations
+      'cts/tests/src',                        # cts test stubs
+      source_root                             # the source for this package
+  ]
+  source_path = [os.path.join(android_root, x) for x in source_path]
+  cmd = ('javadoc -o %s -J-Xmx512m -quiet -doclet DescriptionGenerator -docletpath %s'
+         ' -sourcepath %s ') % (output_file, doclet_path, ':'.join(source_path))
+  sources = []
+
+  def AddFile(sources, folder, names):
+    """Find *.java."""
+    sources.extend([os.path.join(folder, name) for name in names if name.endswith('.java')])
+
+  os.path.walk(os.path.join(android_root, source_root), AddFile, sources)
+  cmd += ' '.join(sources)
+  proc = subprocess.Popen(cmd, shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
+  # read and discard any output
+  proc.communicate()
+  # wait for process to terminate and return exit value
+  return proc.wait()
+
+def RelPath(path, start=os.getcwd()):
+  """Get a relative version of a path.
+
+  This is equivalent to os.path.relpath, which is only available since Python 2.6.
+
+  Args:
+    path: The path to transform.
+    start: The base path. Defaults to the current working directory.
+
+  Returns:
+    A transformed path that is relative to start.
+  """
+  path_dirs = os.path.abspath(path).split(os.path.sep)
+  start_dirs = os.path.abspath(start).split(os.path.sep)
+
+  num_common = len(os.path.commonprefix([start_dirs, path_dirs]))
+
+  result_dirs = ['..'] * (len(start_dirs) - num_common) + path_dirs[num_common:]
+  if result_dirs:
+    return os.path.join(*result_dirs)
+  return start
 
 if __name__ == '__main__':
   builder = CtsBuilder(sys.argv)
diff --git a/tools/utils/startcts b/tools/utils/startcts
index b0cb9ef..4db28b2 100755
--- a/tools/utils/startcts
+++ b/tools/utils/startcts
@@ -53,14 +53,13 @@
 CTS_LIB=${CTS_ROOT}/tools/cts.jar
 JUNIT_LIB=${CTS_ROOT}/tools/junit.jar
 HOSTTEST_LIB=${CTS_ROOT}/tools/hosttestlib.jar
-CTS_TEST_ANNOTATIONS_HOST_LIB=${CTS_ROOT}/tools/CtsTestAnnotationsHostLib.jar
 
 checkFile ${DDM_LIB}
 checkFile ${CTS_LIB}
 checkFile ${JUNIT_LIB}
 checkFile ${HOSTTEST_LIB}
 
-JARS=${CTS_LIB}:${DDM_LIB}:${JUNIT_LIB}:${HOSTTEST_LIB}:${CTS_TEST_ANNOTATIONS_HOST_LIB}
+JARS=${CTS_LIB}:${DDM_LIB}:${JUNIT_LIB}:${HOSTTEST_LIB}
 
 # Add SDK_ROOT to the PATH for backwards compatibility with prior startcts
 # commands that required SDK_ROOT to find adb.