auto import from //depot/cupcake/@135843
diff --git a/test-runner/android/test/ActivityInstrumentationTestCase.java b/test-runner/android/test/ActivityInstrumentationTestCase.java
new file mode 100644
index 0000000..e5a9991
--- /dev/null
+++ b/test-runner/android/test/ActivityInstrumentationTestCase.java
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+package android.test;
+
+import android.app.Activity;
+
+import java.lang.reflect.Field;
+
+/**
+ * This class provides functional testing of a single activity.  The activity under test will
+ * be created using the system infrastructure (by calling InstrumentationTestCase.launchActivity())
+ * and you will then be able to manipulate your Activity directly.  Most of the work is handled
+ * automatically here by {@link #setUp} and {@link #tearDown}.
+ * 
+ * <p>If you prefer an isolated unit test, see {@link android.test.ActivityUnitTestCase}.
+ * 
+ * @deprecated new tests should be written using 
+ * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+ * configuring the Activity under test
+ */
+@Deprecated
+public abstract class ActivityInstrumentationTestCase<T extends Activity> 
+        extends ActivityTestCase {
+    String mPackage;
+    Class<T> mActivityClass;
+    boolean mInitialTouchMode = false;
+
+    /**
+     * @param pkg The package of the instrumentation.
+     * @param activityClass The activity to test.
+     */
+    public ActivityInstrumentationTestCase(String pkg, Class<T> activityClass) {
+        this(pkg, activityClass, false);
+    }
+
+    /**
+     * @param pkg The package of the instrumentation.
+     * @param activityClass The activity to test.
+     * @param initialTouchMode true = in touch mode
+     */
+    public ActivityInstrumentationTestCase(String pkg, Class<T> activityClass, 
+            boolean initialTouchMode) {
+        mPackage = pkg;
+        mActivityClass = activityClass;
+        mInitialTouchMode = initialTouchMode;
+    }
+
+    @Override
+    public T getActivity() {
+        return (T) super.getActivity();
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        // set initial touch mode
+        getInstrumentation().setInTouchMode(mInitialTouchMode);
+        setActivity(launchActivity(mPackage, mActivityClass, null));
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        getActivity().finish();
+        setActivity(null);
+        
+        // Scrub out members - protects against memory leaks in the case where someone 
+        // creates a non-static inner class (thus referencing the test case) and gives it to
+        // someone else to hold onto
+        scrubClass(ActivityInstrumentationTestCase.class);
+
+        super.tearDown();
+    }
+
+    public void testActivityTestCaseSetUpProperly() throws Exception {
+        assertNotNull("activity should be launched successfully", getActivity());
+    }
+}
diff --git a/test-runner/android/test/ActivityInstrumentationTestCase2.java b/test-runner/android/test/ActivityInstrumentationTestCase2.java
new file mode 100644
index 0000000..7a84eca
--- /dev/null
+++ b/test-runner/android/test/ActivityInstrumentationTestCase2.java
@@ -0,0 +1,174 @@
+/*
+ * 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.
+ */
+
+package android.test;
+
+import android.app.Activity;
+import android.content.Intent;
+
+import java.lang.reflect.Method;
+
+/**
+ * This class provides functional testing of a single activity.  The activity under test will
+ * be created using the system infrastructure (by calling InstrumentationTestCase.launchActivity())
+ * and you will then be able to manipulate your Activity directly.
+ * 
+ * <p>Other options supported by this test case include:
+ * <ul>
+ * <li>You can run any test method on the UI thread (see {@link android.test.UiThreadTest}).</li>
+ * <li>You can inject custom Intents into your Activity (see 
+ * {@link #setActivityIntent(Intent)}).</li>
+ * </ul>
+ * 
+ * <p>This class replaces {@link android.test.ActivityInstrumentationTestCase}, which is deprecated.
+ * New tests should be written using this base class.
+ * 
+ * <p>If you prefer an isolated unit test, see {@link android.test.ActivityUnitTestCase}.
+ */
+public abstract class ActivityInstrumentationTestCase2<T extends Activity> 
+        extends ActivityTestCase {
+    String mPackage;
+    Class<T> mActivityClass;
+    boolean mInitialTouchMode = false;
+    Intent mActivityIntent = null;
+
+    /**
+     * @param pkg The package of the instrumentation.
+     * @param activityClass The activity to test.
+     */
+    public ActivityInstrumentationTestCase2(String pkg, Class<T> activityClass) {
+        mPackage = pkg;
+        mActivityClass = activityClass;
+    }
+
+    /**
+     * Get the Activity under test, starting it if necessary.
+     *
+     * For each test method invocation, the Activity will not actually be created until the first
+     * time this method is called. 
+     * 
+     * <p>If you wish to provide custom setup values to your Activity, you may call 
+     * {@link #setActivityIntent(Intent)} and/or {@link #setActivityInitialTouchMode(boolean)} 
+     * before your first call to getActivity().  Calling them after your Activity has 
+     * started will have no effect.
+     *
+     * <p><b>NOTE:</b> Activities under test may not be started from within the UI thread.
+     * If your test method is annotated with {@link android.test.UiThreadTest}, then your Activity
+     * will be started automatically just before your test method is run.  You still call this
+     * method in order to get the Activity under test.
+     * 
+     * @return the Activity under test
+     */
+    @Override
+    public T getActivity() {
+        Activity a = super.getActivity();
+        if (a == null) {
+            // set initial touch mode
+            getInstrumentation().setInTouchMode(mInitialTouchMode);
+            // inject custom intent, if provided
+            if (mActivityIntent == null) {
+                a = launchActivity(mPackage, mActivityClass, null);
+            } else {
+                a = launchActivityWithIntent(mPackage, mActivityClass, mActivityIntent);
+            }
+            setActivity(a);
+        }
+        return (T) a;
+    }
+
+    /**
+     * Call this method before the first call to {@link #getActivity} to inject a customized Intent
+     * into the Activity under test.
+     * 
+     * <p>If you do not call this, the default intent will be provided.  If you call this after
+     * your Activity has been started, it will have no effect.
+     * 
+     * <p><b>NOTE:</b> Activities under test may not be started from within the UI thread.
+     * If your test method is annotated with {@link android.test.UiThreadTest}, then you must call
+     * {@link #setActivityIntent(Intent)} from {@link #setUp()}.
+     *
+     * <p>The default Intent (if this method is not called) is:
+     *  action = {@link Intent#ACTION_MAIN}
+     *  flags = {@link Intent#FLAG_ACTIVITY_NEW_TASK}
+     * All other fields are null or empty.
+     *
+     * @param i The Intent to start the Activity with, or null to reset to the default Intent.
+     */
+    public void setActivityIntent(Intent i) {
+        mActivityIntent = i;
+    }
+    
+    /**
+     * Call this method before the first call to {@link #getActivity} to set the initial touch
+     * mode for the Activity under test.
+     * 
+     * <p>If you do not call this, the touch mode will be false.  If you call this after
+     * your Activity has been started, it will have no effect.
+     * 
+     * <p><b>NOTE:</b> Activities under test may not be started from within the UI thread.
+     * If your test method is annotated with {@link android.test.UiThreadTest}, then you must call
+     * {@link #setActivityInitialTouchMode(boolean)} from {@link #setUp()}.
+     * 
+     * @param initialTouchMode true if the Activity should be placed into "touch mode" when started
+     */
+    public void setActivityInitialTouchMode(boolean initialTouchMode) {
+        mInitialTouchMode = initialTouchMode;
+    }
+    
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        
+        boolean mInitialTouchMode = false;
+        Intent mActivityIntent = null;
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        // Finish the Activity off (unless was never launched anyway)
+        Activity a = super.getActivity();
+        if (a != null) {
+            a.finish();
+            setActivity(null);
+        }
+        
+        // Scrub out members - protects against memory leaks in the case where someone 
+        // creates a non-static inner class (thus referencing the test case) and gives it to
+        // someone else to hold onto
+        scrubClass(ActivityInstrumentationTestCase2.class);
+
+        super.tearDown();
+    }
+
+    /**
+     * Runs the current unit test. If the unit test is annotated with
+     * {@link android.test.UiThreadTest}, force the Activity to be created before switching to
+     * the UI thread.
+     */
+    @Override
+    protected void runTest() throws Throwable {
+        try {
+            Method method = getClass().getMethod(getName(), (Class[]) null);
+            if (method.isAnnotationPresent(UiThreadTest.class)) {
+                getActivity();
+            }
+        } catch (Exception e) {
+            // eat the exception here; super.runTest() will catch it again and handle it properly
+        }
+        super.runTest();
+    }
+
+}
diff --git a/test-runner/android/test/ActivityTestCase.java b/test-runner/android/test/ActivityTestCase.java
new file mode 100644
index 0000000..18bfccc
--- /dev/null
+++ b/test-runner/android/test/ActivityTestCase.java
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+package android.test;
+
+import android.app.Activity;
+
+import java.lang.reflect.Field;
+
+/**
+ * This is common code used to support Activity test cases.  For more useful classes, please see
+ * {@link android.test.ActivityUnitTestCase} and 
+ * {@link android.test.ActivityInstrumentationTestCase}.
+ */
+public abstract class ActivityTestCase extends InstrumentationTestCase {
+
+    /**
+     * The activity that will be set up for use in each test method.
+     */
+    private Activity mActivity;
+
+    /**
+     * @return Returns the activity under test.
+     */
+    protected Activity getActivity() {
+        return mActivity;
+    }
+    
+    /**
+     * Set the activity under test.
+     * @param testActivity The activity under test
+     */
+    protected void setActivity(Activity testActivity) {
+        mActivity = testActivity;
+    }
+    
+    /**
+     * This function is called by various TestCase implementations, at tearDown() time, in order
+     * to scrub out any class variables.  This protects against memory leaks in the case where a
+     * test case creates a non-static inner class (thus referencing the test case) and gives it to
+     * someone else to hold onto.
+     * 
+     * @param testCaseClass The class of the derived TestCase implementation.
+     * 
+     * @throws IllegalAccessException
+     */
+    protected void scrubClass(final Class<?> testCaseClass)
+    throws IllegalAccessException {
+        final Field[] fields = getClass().getDeclaredFields();
+        for (Field field : fields) {
+            final Class<?> fieldClass = field.getDeclaringClass();
+            if (testCaseClass.isAssignableFrom(fieldClass) && !field.getType().isPrimitive()) {
+                try {
+                    field.setAccessible(true);
+                    field.set(this, null);
+                } catch (Exception e) {
+                    android.util.Log.d("TestCase", "Error: Could not nullify field!");
+                }
+
+                if (field.get(this) != null) {
+                    android.util.Log.d("TestCase", "Error: Could not nullify field!");
+                }
+            }
+        }
+    }
+
+    
+
+}
diff --git a/test-runner/android/test/ActivityUnitTestCase.java b/test-runner/android/test/ActivityUnitTestCase.java
new file mode 100644
index 0000000..dfd8fc2
--- /dev/null
+++ b/test-runner/android/test/ActivityUnitTestCase.java
@@ -0,0 +1,340 @@
+/*
+ * 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.
+ */
+
+package android.test;
+
+import android.app.Activity;
+import android.app.Application;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.test.mock.MockApplication;
+import android.view.Window;
+
+/**
+ * This class provides isolated testing of a single activity.  The activity under test will
+ * be created with minimal connection to the system infrastructure, and you can inject mocked or 
+ * wrappered versions of many of Activity's dependencies.  Most of the work is handled
+ * automatically here by {@link #setUp} and {@link #tearDown}.
+ * 
+ * <p>If you prefer a functional test, see {@link android.test.ActivityInstrumentationTestCase}.
+ * 
+ * <p>It must be noted that, as a true unit test, your Activity will not be running in the
+ * normal system and will not participate in the normal interactions with other Activities.  
+ * The following methods should not be called in this configuration - most of them will throw
+ * exceptions:
+ * <ul>
+ * <li>{@link android.app.Activity#createPendingResult(int, Intent, int)}</li>
+ * <li>{@link android.app.Activity#startActivityIfNeeded(Intent, int)}</li>
+ * <li>{@link android.app.Activity#startActivityFromChild(Activity, Intent, int)}</li>
+ * <li>{@link android.app.Activity#startNextMatchingActivity(Intent)}</li>
+ * <li>{@link android.app.Activity#getCallingActivity()}</li>
+ * <li>{@link android.app.Activity#getCallingPackage()}</li>
+ * <li>{@link android.app.Activity#createPendingResult(int, Intent, int)}</li>
+ * <li>{@link android.app.Activity#getTaskId()}</li>
+ * <li>{@link android.app.Activity#isTaskRoot()}</li>
+ * <li>{@link android.app.Activity#moveTaskToBack(boolean)}</li>
+ * <li>{@link android.app.Activity#setPersistent(boolean)}</li>
+ * </ul>
+ * 
+ * <p>The following methods may be called but will not do anything.  For test purposes, you can use 
+ * the methods {@link #getStartedActivityIntent()} and {@link #getStartedActivityRequest()} to 
+ * inspect the parameters that they were called with.
+ * <ul>
+ * <li>{@link android.app.Activity#startActivity(Intent)}</li>
+ * <li>{@link android.app.Activity#startActivityForResult(Intent, int)}</li>
+ * </ul>
+ *
+ * <p>The following methods may be called but will not do anything.  For test purposes, you can use 
+ * the methods {@link #isFinishCalled()} and {@link #getFinishedActivityRequest()} to inspect the 
+ * parameters that they were called with.
+ * <ul>
+ * <li>{@link android.app.Activity#finish()}</li>
+ * <li>{@link android.app.Activity#finishFromChild(Activity child)}</li>
+ * <li>{@link android.app.Activity#finishActivity(int requestCode)}</li>
+ * </ul>
+ *
+ */
+public abstract class ActivityUnitTestCase<T extends Activity> 
+        extends ActivityTestCase {
+
+    private Class<T> mActivityClass;
+
+    private Context mActivityContext;
+    private Application mApplication;
+    private MockParent mMockParent;
+
+    private boolean mAttached = false;
+    private boolean mCreated = false;
+
+    public ActivityUnitTestCase(Class<T> activityClass) {
+        mActivityClass = activityClass;
+    }
+
+    @Override
+    public T getActivity() {
+        return (T) super.getActivity();
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        // default value for target context, as a default
+      mActivityContext = getInstrumentation().getTargetContext();
+    }
+    
+    /**
+     * Start the activity under test, in the same way as if it was started by
+     * {@link android.content.Context#startActivity Context.startActivity()}, providing the 
+     * arguments it supplied.  When you use this method to start the activity, it will automatically
+     * be stopped by {@link #tearDown}.
+     * 
+     * <p>This method will call onCreate(), but if you wish to further exercise Activity life 
+     * cycle methods, you must call them yourself from your test case.
+     * 
+     * <p><i>Do not call from your setUp() method.  You must call this method from each of your
+     * test methods.</i>
+     *  
+     * @param intent The Intent as if supplied to {@link android.content.Context#startActivity}.
+     * @param savedInstanceState The instance state, if you are simulating this part of the life
+     * cycle.  Typically null.
+     * @param lastNonConfigurationInstance This Object will be available to the 
+     * Activity if it calls {@link android.app.Activity#getLastNonConfigurationInstance()}.  
+     * Typically null.
+     * @return Returns the Activity that was created
+     */
+    protected T startActivity(Intent intent, Bundle savedInstanceState,
+            Object lastNonConfigurationInstance) {
+        assertFalse("Activity already created", mCreated);
+        
+        if (!mAttached) {
+            assertNotNull(mActivityClass);
+            setActivity(null);
+            T newActivity = null;
+            try {
+                IBinder token = null;
+                if (mApplication == null) {
+                    setApplication(new MockApplication());
+                }
+                ComponentName cn = new ComponentName(mActivityClass.getPackage().getName(), 
+                        mActivityClass.getName());
+                intent.setComponent(cn);
+                ActivityInfo info = null;
+                CharSequence title = mActivityClass.getName();
+                mMockParent = new MockParent();
+                String id = null;
+                        
+                newActivity = (T) getInstrumentation().newActivity(mActivityClass, mActivityContext,
+                        token, mApplication, intent, info, title, mMockParent, id,
+                        lastNonConfigurationInstance);
+            } catch (Exception e) {
+                assertNotNull(newActivity);
+            }
+            
+            assertNotNull(newActivity);
+            setActivity(newActivity);
+            
+            mAttached = true;
+        }
+        
+        T result = getActivity();
+        if (result != null) {
+            getInstrumentation().callActivityOnCreate(getActivity(), savedInstanceState);
+            mCreated = true;
+        }
+        return result;
+    }
+    
+    @Override
+    protected void tearDown() throws Exception {
+        
+        setActivity(null);
+        
+        // Scrub out members - protects against memory leaks in the case where someone 
+        // creates a non-static inner class (thus referencing the test case) and gives it to
+        // someone else to hold onto
+        scrubClass(ActivityInstrumentationTestCase.class);
+
+        super.tearDown();
+    }
+    
+    /**
+     * Set the application for use during the test.  You must call this function before calling 
+     * {@link #startActivity}.  If your test does not call this method,
+     * @param application The Application object that will be injected into the Activity under test.
+     */
+    public void setApplication(Application application) {
+        mApplication = application;
+    }
+
+    /**
+     * If you wish to inject a Mock, Isolated, or otherwise altered context, you can do so
+     * here.  You must call this function before calling {@link #startActivity}.  If you wish to
+     * obtain a real Context, as a building block, use getInstrumentation().getTargetContext().
+     */
+    public void setActivityContext(Context activityContext) {
+        mActivityContext = activityContext;
+    }
+
+    /**
+     * This method will return the value if your Activity under test calls 
+     * {@link android.app.Activity#setRequestedOrientation}.
+     */
+    public int getRequestedOrientation() {
+        if (mMockParent != null) {
+            return mMockParent.mRequestedOrientation;
+        }
+        return 0;
+    }
+    
+    /**
+     * This method will return the launch intent if your Activity under test calls 
+     * {@link android.app.Activity#startActivity(Intent)} or 
+     * {@link android.app.Activity#startActivityForResult(Intent, int)}.
+     * @return The Intent provided in the start call, or null if no start call was made.
+     */
+    public Intent getStartedActivityIntent() {
+        if (mMockParent != null) {
+            return mMockParent.mStartedActivityIntent;
+        }
+        return null;
+    }
+    
+    /**
+     * This method will return the launch request code if your Activity under test calls 
+     * {@link android.app.Activity#startActivityForResult(Intent, int)}.
+     * @return The request code provided in the start call, or -1 if no start call was made.
+     */
+    public int getStartedActivityRequest() {
+        if (mMockParent != null) {
+            return mMockParent.mStartedActivityRequest;
+        }
+        return 0;
+    }
+
+    /**
+     * This method will notify you if the Activity under test called 
+     * {@link android.app.Activity#finish()}, 
+     * {@link android.app.Activity#finishFromChild(Activity)}, or 
+     * {@link android.app.Activity#finishActivity(int)}.
+     * @return Returns true if one of the listed finish methods was called.
+     */
+    public boolean isFinishCalled() {
+        if (mMockParent != null) {
+            return mMockParent.mFinished;
+        }
+        return false;
+    }
+    
+    /**
+     * This method will return the request code if the Activity under test called 
+     * {@link android.app.Activity#finishActivity(int)}.
+     * @return The request code provided in the start call, or -1 if no finish call was made.
+     */
+    public int getFinishedActivityRequest() {
+        if (mMockParent != null) {
+            return mMockParent.mFinishedActivityRequest;
+        }
+        return 0;
+    }
+    
+    /**
+     * This mock Activity represents the "parent" activity.  By injecting this, we allow the user
+     * to call a few more Activity methods, including:
+     * <ul>
+     * <li>{@link android.app.Activity#getRequestedOrientation()}</li>
+     * <li>{@link android.app.Activity#setRequestedOrientation(int)}</li>
+     * <li>{@link android.app.Activity#finish()}</li>
+     * <li>{@link android.app.Activity#finishActivity(int requestCode)}</li>
+     * <li>{@link android.app.Activity#finishFromChild(Activity child)}</li>
+     * </ul>
+     * 
+     * TODO: Make this overrideable, and the unit test can look for calls to other methods
+     */
+    private static class MockParent extends Activity {
+
+        public int mRequestedOrientation = 0;
+        public Intent mStartedActivityIntent = null;
+        public int mStartedActivityRequest = -1;
+        public boolean mFinished = false;
+        public int mFinishedActivityRequest = -1;
+
+        /**
+         * Implementing in the parent allows the user to call this function on the tested activity.
+         */
+        @Override
+        public void setRequestedOrientation(int requestedOrientation) {
+            mRequestedOrientation = requestedOrientation;
+        }
+
+        /**
+         * Implementing in the parent allows the user to call this function on the tested activity.
+         */
+        @Override
+        public int getRequestedOrientation() {
+            return mRequestedOrientation;
+        }
+
+        /**
+         * By returning null here, we inhibit the creation of any "container" for the window.
+         */
+        @Override
+        public Window getWindow() {
+            return null;
+        }
+        
+        /**
+         * By defining this in the parent, we allow the tested activity to call
+         * <ul>
+         * <li>{@link android.app.Activity#startActivity(Intent)}</li>
+         * <li>{@link android.app.Activity#startActivityForResult(Intent, int)}</li>
+         * </ul>
+         */
+        @Override
+        public void startActivityFromChild(Activity child, Intent intent, int requestCode) {
+            mStartedActivityIntent = intent;
+            mStartedActivityRequest = requestCode;
+        }
+        
+        /**
+         * By defining this in the parent, we allow the tested activity to call
+         * <ul>
+         * <li>{@link android.app.Activity#finish()}</li>
+         * <li>{@link android.app.Activity#finishFromChild(Activity child)}</li>
+         * </ul>
+         */
+        @Override
+        public void finishFromChild(Activity child) {
+            mFinished = true;
+        }
+
+        /**
+         * By defining this in the parent, we allow the tested activity to call
+         * <ul>
+         * <li>{@link android.app.Activity#finishActivity(int requestCode)}</li>
+         * </ul>
+         */
+        @Override
+        public void finishActivityFromChild(Activity child, int requestCode) {
+            mFinished = true;
+            mFinishedActivityRequest = requestCode;
+        }
+    }
+}
diff --git a/test-runner/android/test/AndroidTestRunner.java b/test-runner/android/test/AndroidTestRunner.java
new file mode 100644
index 0000000..79cedb0
--- /dev/null
+++ b/test-runner/android/test/AndroidTestRunner.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2007 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.test;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import com.google.android.collect.Lists;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestListener;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+import junit.runner.BaseTestRunner;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+
+public class AndroidTestRunner extends BaseTestRunner {
+
+    private TestResult mTestResult;
+    private String mTestClassName;
+    private List<TestCase> mTestCases;
+    private Context mContext;
+    private boolean mSkipExecution = false;
+
+    private List<TestListener> mTestListeners = Lists.newArrayList();
+    private Instrumentation mInstrumentation;
+
+    @SuppressWarnings("unchecked")
+    public void setTestClassName(String testClassName, String testMethodName) {
+        Class testClass = loadTestClass(testClassName);
+
+        if (shouldRunSingleTestMethod(testMethodName, testClass)) {
+            TestCase testCase = buildSingleTestMethod(testClass, testMethodName);
+            mTestCases = Lists.newArrayList(testCase);
+            mTestClassName = testClass.getSimpleName();
+        } else {
+            setTest(getTest(testClass), testClass);
+        }
+    }
+
+    public void setTest(Test test) {
+        setTest(test, test.getClass());
+    }
+
+    private void setTest(Test test, Class<? extends Test> testClass) {
+        mTestCases = (List<TestCase>) TestCaseUtil.getTests(test, true);
+        if (TestSuite.class.isAssignableFrom(testClass)) {
+            mTestClassName = TestCaseUtil.getTestName(test);
+        } else {
+            mTestClassName = testClass.getSimpleName();
+        }
+    }
+
+    public void clearTestListeners() {
+        mTestListeners.clear();
+    }
+
+    public void addTestListener(TestListener testListener) {
+        if (testListener != null) {
+            mTestListeners.add(testListener);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private Class<? extends Test> loadTestClass(String testClassName) {
+        try {
+            return (Class<? extends Test>) mContext.getClassLoader().loadClass(testClassName);
+        } catch (ClassNotFoundException e) {
+            runFailed("Could not find test class. Class: " + testClassName);
+        }
+        return null;
+    }
+
+    private TestCase buildSingleTestMethod(Class testClass, String testMethodName) {
+        try {
+            TestCase testCase = (TestCase) testClass.newInstance();
+            testCase.setName(testMethodName);
+            return testCase;
+        } catch (IllegalAccessException e) {
+            runFailed("Could not access test class. Class: " + testClass.getName());
+        } catch (InstantiationException e) {
+            runFailed("Could not instantiate test class. Class: " + testClass.getName());
+        }
+
+        return null;
+    }
+
+    private boolean shouldRunSingleTestMethod(String testMethodName,
+            Class<? extends Test> testClass) {
+        return testMethodName != null && TestCase.class.isAssignableFrom(testClass);
+    }
+
+    private Test getTest(Class clazz) {
+        if (TestSuiteProvider.class.isAssignableFrom(clazz)) {
+            try {
+                TestSuiteProvider testSuiteProvider =
+                        (TestSuiteProvider) clazz.getConstructor().newInstance();
+                return testSuiteProvider.getTestSuite();
+            } catch (InstantiationException e) {
+                runFailed("Could not instantiate test suite provider. Class: " + clazz.getName());
+            } catch (IllegalAccessException e) {
+                runFailed("Illegal access of test suite provider. Class: " + clazz.getName());
+            } catch (InvocationTargetException e) {
+                runFailed("Invocation exception test suite provider. Class: " + clazz.getName());
+            } catch (NoSuchMethodException e) {
+                runFailed("No such method on test suite provider. Class: " + clazz.getName());
+            }
+        }
+        return getTest(clazz.getName());
+    }
+
+    protected TestResult createTestResult() {
+        if (mSkipExecution) {
+            return new NoExecTestResult();
+        }
+        return new TestResult();
+    }
+    
+    void setSkipExecution(boolean skip) {
+        mSkipExecution = skip;
+    }
+
+    public List<TestCase> getTestCases() {
+        return mTestCases;
+    }
+
+    public String getTestClassName() {
+        return mTestClassName;
+    }
+
+    public TestResult getTestResult() {
+        return mTestResult;
+    }
+
+    public void runTest() {
+        runTest(createTestResult());
+    }
+
+    public void runTest(TestResult testResult) {
+        mTestResult = testResult;
+
+        for (TestListener testListener : mTestListeners) {
+            mTestResult.addListener(testListener);
+        }
+
+        for (TestCase testCase : mTestCases) {
+            setContextIfAndroidTestCase(testCase, mContext);
+            setInstrumentationIfInstrumentationTestCase(testCase, mInstrumentation);
+            testCase.run(mTestResult);
+        }
+    }
+
+    private void setContextIfAndroidTestCase(Test test, Context context) {
+        if (AndroidTestCase.class.isAssignableFrom(test.getClass())) {
+            ((AndroidTestCase) test).setContext(context);
+        }
+    }
+
+    public void setContext(Context context) {
+        mContext = context;
+    }
+
+    private void setInstrumentationIfInstrumentationTestCase(
+            Test test, Instrumentation instrumentation) {
+        if (InstrumentationTestCase.class.isAssignableFrom(test.getClass())) {
+            ((InstrumentationTestCase) test).injectInsrumentation(instrumentation);
+        }
+    }
+
+    public void setInstrumentaiton(Instrumentation instrumentation) {
+        mInstrumentation = instrumentation;
+    }
+
+    @Override
+    protected Class loadSuiteClass(String suiteClassName) throws ClassNotFoundException {
+        return mContext.getClassLoader().loadClass(suiteClassName);
+    }
+
+    public void testStarted(String testName) {
+    }
+
+    public void testEnded(String testName) {
+    }
+
+    public void testFailed(int status, Test test, Throwable t) {
+    }
+
+    protected void runFailed(String message) {
+        throw new RuntimeException(message);
+    }
+}
diff --git a/test-runner/android/test/ApplicationTestCase.java b/test-runner/android/test/ApplicationTestCase.java
new file mode 100644
index 0000000..31b226a
--- /dev/null
+++ b/test-runner/android/test/ApplicationTestCase.java
@@ -0,0 +1,177 @@
+/*
+ * 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.
+ */
+
+package android.test;
+
+import java.lang.reflect.Field;
+
+import android.app.Application;
+import android.app.Instrumentation;
+import android.content.Context;
+
+/**
+ * This test case provides a framework in which you can test Application classes in
+ * a controlled environment.  It provides basic support for the lifecycle of a
+ * Application, and hooks by which you can inject various dependencies and control
+ * the environment in which your Application is tested.
+ *
+ * <p><b>Lifecycle Support.</b>
+ * Every Application is designed to be accessed within a specific sequence of
+ * method calls (see {@link android.app.Application} for more details).
+ * In order to support the lifecycle of a Application, this test case will make the
+ * following calls at the following times.
+ *
+ * <ul><li>The test case will not call onCreate() until your test calls 
+ * {@link #createApplication()}.  This gives you a chance
+ * to set up or adjust any additional framework or test logic before
+ * onCreate().</li>
+ * <li>After your test completes, the test case {@link #tearDown} method is
+ * automatically called, and it will stop & destroy your application by calling its
+ * onDestroy() method.</li>
+ * </ul>
+ * 
+ * <p><b>Dependency Injection.</b>
+ * Every Application has one inherent dependency, the {@link android.content.Context Context} in
+ * which it runs.
+ * This framework allows you to inject a modified, mock, or isolated replacement for this 
+ * dependencies, and thus perform a true unit test.
+ * 
+ * <p>If simply run your tests as-is, your Application will be injected with a fully-functional
+ * Context.
+ * You can create and inject alternative types of Contexts by calling 
+ * {@link AndroidTestCase#setContext(Context) setContext()}.  You must do this <i>before</i> calling
+ * startApplication().  The test framework provides a
+ * number of alternatives for Context, including {@link android.test.mock.MockContext MockContext}, 
+ * {@link android.test.RenamingDelegatingContext RenamingDelegatingContext}, and 
+ * {@link android.content.ContextWrapper ContextWrapper}.
+ */
+public abstract class ApplicationTestCase<T extends Application> extends AndroidTestCase {
+
+    Class<T> mApplicationClass;
+
+    private Context mSystemContext;
+
+    public ApplicationTestCase(Class<T> applicationClass) {
+        mApplicationClass = applicationClass;
+    }
+
+    private T mApplication;
+    private boolean mAttached = false;
+    private boolean mCreated = false;
+
+    /**
+     * @return Returns the actual Application under test.
+     */
+    public T getApplication() {
+        return mApplication;
+    }
+
+    /**
+     * This will do the work to instantiate the Application under test.  After this, your test 
+     * code must also start and stop the Application.
+     */
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        
+        // get the real context, before the individual tests have a chance to muck with it
+        mSystemContext = getContext();
+    }
+    
+    /**
+     * Load and attach the application under test.
+     */
+    private void setupApplication() {
+        mApplication = null;
+        try {
+            mApplication = (T) Instrumentation.newApplication(mApplicationClass, getContext());
+        } catch (Exception e) {
+            assertNotNull(mApplication);
+        }
+        mAttached = true;
+    }
+    
+    /**
+     * Start the Application under test, in the same way as if it was started by the system.  
+     * If you use this method to start the Application, it will automatically
+     * be stopped by {@link #tearDown}.  If you wish to inject a specialized Context for your
+     * test, by calling {@link AndroidTestCase#setContext(Context) setContext()}, 
+     * you must do so  before calling this method.
+     */
+    final protected void createApplication() {
+        assertFalse(mCreated);
+        
+        if (!mAttached) {
+            setupApplication();
+        }
+        assertNotNull(mApplication);
+        
+        mApplication.onCreate();
+        mCreated = true;
+    }
+    
+    /**
+     * This will make the necessary calls to terminate the Application under test (it will
+     * call onTerminate().  Ordinarily this will be called automatically (by {@link #tearDown}, but
+     * you can call it directly from your test in order to check for proper shutdown behaviors.
+     */
+    final protected void terminateApplication() {
+        if (mCreated) {
+            mApplication.onTerminate();
+        }
+    }
+    
+    /**
+     * Shuts down the Application under test.  Also makes sure all resources are cleaned up and 
+     * garbage collected before moving on to the next
+     * test.  Subclasses that override this method should make sure they call super.tearDown()
+     * at the end of the overriding method.
+     * 
+     * @throws Exception
+     */
+    @Override
+    protected void tearDown() throws Exception {
+        terminateApplication();
+        mApplication = null;
+
+        // Scrub out members - protects against memory leaks in the case where someone 
+        // creates a non-static inner class (thus referencing the test case) and gives it to
+        // someone else to hold onto
+        scrubClass(ApplicationTestCase.class);
+
+        super.tearDown();
+    }
+
+    /**
+     * Return a real (not mocked or instrumented) system Context that can be used when generating
+     * Mock or other Context objects for your Application under test.
+     * 
+     * @return Returns a reference to a normal Context.
+     */
+    public Context getSystemContext() {
+        return mSystemContext;
+    }
+
+    /**
+     * This test simply confirms that the Application class can be instantiated properly.
+     * 
+     * @throws Exception
+     */
+    final public void testApplicationTestCaseSetUpProperly() throws Exception {
+        setupApplication();
+        assertNotNull("Application class could not be instantiated successfully", mApplication);
+    }
+}
diff --git a/test-runner/android/test/AssertionFailedError.java b/test-runner/android/test/AssertionFailedError.java
new file mode 100644
index 0000000..7af5806
--- /dev/null
+++ b/test-runner/android/test/AssertionFailedError.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package android.test;
+
+/**
+ * Thrown when an assertion failed.
+ * 
+ * Note:  Most users of this class should simply use junit.framework.AssertionFailedError,
+ * which provides the same functionality.
+ */
+public class AssertionFailedError extends Error {
+    
+    /**
+     * It is more typical to call {@link #AssertionFailedError(String)}.
+     */
+    public AssertionFailedError() {
+    }
+    
+    public AssertionFailedError(String errorMessage) {
+        super(errorMessage);
+    }
+}
diff --git a/test-runner/android/test/BundlePrinter.java b/test-runner/android/test/BundlePrinter.java
new file mode 100644
index 0000000..96213e7
--- /dev/null
+++ b/test-runner/android/test/BundlePrinter.java
@@ -0,0 +1,63 @@
+package android.test;
+
+import java.io.PrintStream;
+
+import android.os.Bundle;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.runner.BaseTestRunner;
+import junit.textui.ResultPrinter;
+
+
+/**
+ * Subclass of ResultPrinter that adds test case results to a bundle.
+ * 
+ * {@hide} - This class is deprecated, and will be going away.  Please don't use it.
+ */
+public class BundlePrinter extends ResultPrinter {
+
+    private Bundle mResults;
+    private boolean mFailure;
+    private boolean mError;
+    
+    public BundlePrinter(PrintStream writer, Bundle result) {
+        super(writer);
+        mResults = result;
+    }
+    
+    @Override
+    public void addError(Test test, Throwable t) {
+        mResults.putString(getComboName(test), BaseTestRunner.getFilteredTrace(t));
+        mFailure = true;
+        super.addError(test, t);
+    }
+
+    @Override
+    public void addFailure(Test test, AssertionFailedError t) {
+        mResults.putString(getComboName(test), BaseTestRunner.getFilteredTrace(t));
+        mError = true;
+        super.addFailure(test, t);
+    }
+
+    @Override
+    public void endTest(Test test) {
+        if (!mFailure && !mError) {
+            mResults.putString(getComboName(test), "passed");
+        }
+        super.endTest(test);
+    }
+
+    @Override
+    public void startTest(Test test) {
+        mFailure = false;
+        mError = false;
+        super.startTest(test);
+    }
+    
+    private String getComboName(Test test) {
+        return test.getClass().getName() + ":" + ((TestCase) test).getName();
+    }
+    
+}
diff --git a/test-runner/android/test/BundleTestListener.java b/test-runner/android/test/BundleTestListener.java
new file mode 100644
index 0000000..772713f
--- /dev/null
+++ b/test-runner/android/test/BundleTestListener.java
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+package android.test;
+
+import junit.framework.*;
+import junit.framework.TestCase;
+import junit.runner.BaseTestRunner;
+import android.os.Bundle;
+
+/**
+ * A {@link TestListener} that adds test case results to a bundle.
+ * 
+ * {@hide} - This class is deprecated, and will be going away.  Please don't use it.
+ */
+public class BundleTestListener implements TestListener {
+
+    private Bundle mBundle;
+    private boolean mFailed;
+
+    public BundleTestListener(Bundle bundle) {
+        mBundle = bundle;
+    }
+
+
+    public void addError(Test test, Throwable t) {
+        mBundle.putString(getComboName(test), BaseTestRunner.getFilteredTrace(t));
+        mFailed = true;
+    }
+
+    public void addFailure(Test test, junit.framework.AssertionFailedError t) {
+        mBundle.putString(getComboName(test), BaseTestRunner.getFilteredTrace(t));
+        mFailed = true;
+    }
+
+    public void endTest(Test test) {
+        if (!mFailed) {
+            mBundle.putString(getComboName(test), "passed");
+        }
+    }
+
+    public void startTest(Test test) {
+        mFailed = false;
+    }
+
+    private String getComboName(Test test) {
+        return test.getClass().getName() + ":" + ((TestCase) test).getName();
+    }
+
+}
diff --git a/test-runner/android/test/ClassPathPackageInfo.java b/test-runner/android/test/ClassPathPackageInfo.java
new file mode 100644
index 0000000..1f6e647
--- /dev/null
+++ b/test-runner/android/test/ClassPathPackageInfo.java
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+package android.test;
+
+import com.google.android.collect.Sets;
+
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * The Package object doesn't allow you to iterate over the contained
+ * classes and subpackages of that package.  This is a version that does.
+ * 
+ * {@hide} Not needed for 1.0 SDK.
+ */
+public class ClassPathPackageInfo {
+
+    private final ClassPathPackageInfoSource source;
+    private final String packageName;
+    private final Set<String> subpackageNames;
+    private final Set<Class<?>> topLevelClasses;
+
+    ClassPathPackageInfo(ClassPathPackageInfoSource source, String packageName,
+            Set<String> subpackageNames, Set<Class<?>> topLevelClasses) {
+        this.source = source;
+        this.packageName = packageName;
+        this.subpackageNames = Collections.unmodifiableSet(subpackageNames);
+        this.topLevelClasses = Collections.unmodifiableSet(topLevelClasses);
+    }
+
+    public Set<ClassPathPackageInfo> getSubpackages() {
+        Set<ClassPathPackageInfo> info = Sets.newHashSet();
+        for (String name : subpackageNames) {
+            info.add(source.getPackageInfo(name));
+        }
+        return info;
+    }
+
+    public Set<Class<?>> getTopLevelClassesRecursive() {
+        Set<Class<?>> set = Sets.newHashSet();
+        addTopLevelClassesTo(set);
+        return set;
+    }
+
+    private void addTopLevelClassesTo(Set<Class<?>> set) {
+        set.addAll(topLevelClasses);
+        for (ClassPathPackageInfo info : getSubpackages()) {
+            info.addTopLevelClassesTo(set);
+        }
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof ClassPathPackageInfo) {
+            ClassPathPackageInfo that = (ClassPathPackageInfo) obj;
+            return (this.packageName).equals(that.packageName);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return packageName.hashCode();
+    }
+}
diff --git a/test-runner/android/test/ClassPathPackageInfoSource.java b/test-runner/android/test/ClassPathPackageInfoSource.java
new file mode 100644
index 0000000..12bc7f3
--- /dev/null
+++ b/test-runner/android/test/ClassPathPackageInfoSource.java
@@ -0,0 +1,320 @@
+/*
+ * 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.
+ */
+
+package android.test;
+
+import android.util.Config;
+import android.util.Log;
+import com.google.android.collect.Maps;
+import com.google.android.collect.Sets;
+import dalvik.system.DexFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.regex.Pattern;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * Generate {@link ClassPathPackageInfo}s by scanning apk paths.
+ * 
+ * {@hide} Not needed for 1.0 SDK.
+ */
+public class ClassPathPackageInfoSource {
+
+    private static final String CLASS_EXTENSION = ".class";
+
+    private static final ClassLoader CLASS_LOADER
+            = ClassPathPackageInfoSource.class.getClassLoader();
+
+    private final SimpleCache<String, ClassPathPackageInfo> cache =
+            new SimpleCache<String, ClassPathPackageInfo>() {
+                @Override
+                protected ClassPathPackageInfo load(String pkgName) {
+                    return createPackageInfo(pkgName);
+                }
+            };
+
+    // The class path of the running application
+    private final String[] classPath;
+    private static String[] apkPaths;
+
+    // A cache of jar file contents
+    private final Map<File, Set<String>> jarFiles = Maps.newHashMap();
+    private ClassLoader classLoader;
+
+    ClassPathPackageInfoSource() {
+        classPath = getClassPath();
+    }
+
+
+    public static void setApkPaths(String[] apkPaths) {
+        ClassPathPackageInfoSource.apkPaths = apkPaths;
+    }
+
+    public ClassPathPackageInfo getPackageInfo(String pkgName) {
+        return cache.get(pkgName);
+    }
+
+    private ClassPathPackageInfo createPackageInfo(String packageName) {
+        Set<String> subpackageNames = new TreeSet<String>();
+        Set<String> classNames = new TreeSet<String>();
+        Set<Class<?>> topLevelClasses = Sets.newHashSet();
+        findClasses(packageName, classNames, subpackageNames);
+        for (String className : classNames) {
+            if (className.endsWith(".R") || className.endsWith(".Manifest")) {
+                // Don't try to load classes that are generated. They usually aren't in test apks.
+                continue;
+            }
+            
+            try {
+                // We get errors in the emulator if we don't use the caller's class loader.
+                topLevelClasses.add(Class.forName(className, false,
+                        (classLoader != null) ? classLoader : CLASS_LOADER));
+            } catch (ClassNotFoundException e) {
+                // Should not happen unless there is a generated class that is not included in
+                // the .apk.
+                Log.w("ClassPathPackageInfoSource", "Cannot load class. "
+                        + "Make sure it is in your apk. Class name: '" + className
+                        + "'. Message: " + e.getMessage(), e);
+            }
+        }
+        return new ClassPathPackageInfo(this, packageName, subpackageNames,
+                topLevelClasses);
+    }
+
+    /**
+     * Finds all classes and sub packages that are below the packageName and
+     * add them to the respective sets. Searches the package on the whole class
+     * path.
+     */
+    private void findClasses(String packageName, Set<String> classNames,
+            Set<String> subpackageNames) {
+        String packagePrefix = packageName + '.';
+        String pathPrefix = packagePrefix.replace('.', '/');
+
+        for (String entryName : classPath) {
+            File classPathEntry = new File(entryName);
+
+            // Forge may not have brought over every item in the classpath. Be
+            // polite and ignore missing entries.
+            if (classPathEntry.exists()) {
+                try {
+                    if (entryName.endsWith(".apk")) {
+                        findClassesInApk(entryName, packageName, classNames, subpackageNames);
+                    } else if ("true".equals(System.getProperty("android.vm.dexfile", "false"))) {
+                        // If the vm supports dex files then scan the directories that contain
+                        // apk files. 
+                        for (String apkPath : apkPaths) {
+                            File file = new File(apkPath);
+                            scanForApkFiles(file, packageName, classNames, subpackageNames);
+                        }
+                    } else if (entryName.endsWith(".jar")) {
+                        findClassesInJar(classPathEntry, pathPrefix,
+                                classNames, subpackageNames);
+                    } else if (classPathEntry.isDirectory()) {
+                        findClassesInDirectory(classPathEntry, packagePrefix, pathPrefix,
+                                classNames, subpackageNames);
+                    } else {
+                        throw new AssertionError("Don't understand classpath entry " +
+                                classPathEntry);
+                    }
+                } catch (IOException e) {
+                    throw new AssertionError("Can't read classpath entry " +
+                            entryName + ": " + e.getMessage());
+                }
+            }
+        }
+    }
+
+    private void scanForApkFiles(File source, String packageName,
+            Set<String> classNames, Set<String> subpackageNames) throws IOException {
+        if (source.getPath().endsWith(".apk")) {
+            findClassesInApk(source.getPath(), packageName, classNames, subpackageNames);
+        } else {
+            File[] files = source.listFiles();
+            if (files != null) {
+                for (File file : files) {
+                    scanForApkFiles(file, packageName, classNames, subpackageNames);
+                }
+            }
+        }
+    }
+
+    /**
+     * Finds all classes and sub packages that are below the packageName and
+     * add them to the respective sets. Searches the package in a class directory.
+     */
+    private void findClassesInDirectory(File classDir,
+            String packagePrefix, String pathPrefix, Set<String> classNames,
+            Set<String> subpackageNames)
+            throws IOException {
+        File directory = new File(classDir, pathPrefix);
+
+        if (directory.exists()) {
+            for (File f : directory.listFiles()) {
+                String name = f.getName();
+                if (name.endsWith(CLASS_EXTENSION) && isToplevelClass(name)) {
+                    classNames.add(packagePrefix + getClassName(name));
+                } else if (f.isDirectory()) {
+                    subpackageNames.add(packagePrefix + name);
+                }
+            }
+        }
+    }
+
+    /**
+     * Finds all classes and sub packages that are below the packageName and
+     * add them to the respective sets. Searches the package in a single jar file.
+     */
+    private void findClassesInJar(File jarFile, String pathPrefix,
+            Set<String> classNames, Set<String> subpackageNames)
+            throws IOException {
+        Set<String> entryNames = getJarEntries(jarFile);
+        // check if the Jar contains the package.
+        if (!entryNames.contains(pathPrefix)) {
+            return;
+        }
+        int prefixLength = pathPrefix.length();
+        for (String entryName : entryNames) {
+            if (entryName.startsWith(pathPrefix)) {
+                if (entryName.endsWith(CLASS_EXTENSION)) {
+                    // check if the class is in the package itself or in one of its
+                    // subpackages.
+                    int index = entryName.indexOf('/', prefixLength);
+                    if (index >= 0) {
+                        String p = entryName.substring(0, index).replace('/', '.');
+                        subpackageNames.add(p);
+                    } else if (isToplevelClass(entryName)) {
+                        classNames.add(getClassName(entryName).replace('/', '.'));
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Finds all classes and sub packages that are below the packageName and
+     * add them to the respective sets. Searches the package in a single apk file.
+     */
+    private void findClassesInApk(String apkPath, String packageName,
+            Set<String> classNames, Set<String> subpackageNames)
+            throws IOException {
+
+        DexFile dexFile = null;
+        try {
+            dexFile = new DexFile(apkPath);
+            Enumeration<String> apkClassNames = dexFile.entries();
+            while (apkClassNames.hasMoreElements()) {
+                String className = apkClassNames.nextElement();
+
+                if (className.startsWith(packageName)) {
+                    int lastPackageSeparator = className.lastIndexOf('.');
+                    String subPackageName = className.substring(0, lastPackageSeparator);
+                    if (subPackageName.length() > packageName.length()) {
+                        subpackageNames.add(subPackageName);
+                    } else if (isToplevelClass(className)) {
+                        classNames.add(className);
+                    }
+                }
+            }
+        } catch (IOException e) {
+            if (Config.LOGV) {
+                Log.w("ClassPathPackageInfoSource",
+                        "Error finding classes at apk path: " + apkPath, e);
+            }
+        } finally {
+            if (dexFile != null) {
+                // Todo: figure out why closing causes a dalvik error resulting in vm shutdown.
+//                dexFile.close();
+            }
+        }
+    }
+
+    /**
+     * Gets the class and package entries from a Jar.
+     */
+    private Set<String> getJarEntries(File jarFile)
+            throws IOException {
+        Set<String> entryNames = jarFiles.get(jarFile);
+        if (entryNames == null) {
+            entryNames = Sets.newHashSet();
+            ZipFile zipFile = new ZipFile(jarFile);
+            Enumeration<? extends ZipEntry> entries = zipFile.entries();
+            while (entries.hasMoreElements()) {
+                String entryName = entries.nextElement().getName();
+                if (entryName.endsWith(CLASS_EXTENSION)) {
+                    // add the entry name of the class
+                    entryNames.add(entryName);
+
+                    // add the entry name of the classes package, i.e. the entry name of
+                    // the directory that the class is in. Used to quickly skip jar files
+                    // if they do not contain a certain package.
+                    //
+                    // Also add parent packages so that a JAR that contains
+                    // pkg1/pkg2/Foo.class will be marked as containing pkg1/ in addition
+                    // to pkg1/pkg2/ and pkg1/pkg2/Foo.class.  We're still interested in
+                    // JAR files that contains subpackages of a given package, even if
+                    // an intermediate package contains no direct classes.
+                    //
+                    // Classes in the default package will cause a single package named
+                    // "" to be added instead.
+                    int lastIndex = entryName.lastIndexOf('/');
+                    do {
+                        String packageName = entryName.substring(0, lastIndex + 1);
+                        entryNames.add(packageName);
+                        lastIndex = entryName.lastIndexOf('/', lastIndex - 1);
+                    } while (lastIndex > 0);
+                }
+            }
+            jarFiles.put(jarFile, entryNames);
+        }
+        return entryNames;
+    }
+
+    /**
+     * Checks if a given file name represents a toplevel class.
+     */
+    private static boolean isToplevelClass(String fileName) {
+        return fileName.indexOf('$') < 0;
+    }
+
+    /**
+     * Given the absolute path of a class file, return the class name.
+     */
+    private static String getClassName(String className) {
+        int classNameEnd = className.length() - CLASS_EXTENSION.length();
+        return className.substring(0, classNameEnd);
+    }
+
+    /**
+     * Gets the class path from the System Property "java.class.path" and splits
+     * it up into the individual elements.
+     */
+    private static String[] getClassPath() {
+        String classPath = System.getProperty("java.class.path");
+        String separator = System.getProperty("path.separator", ":");
+        return classPath.split(Pattern.quote(separator));
+    }
+
+    public void setClassLoader(ClassLoader classLoader) {
+        this.classLoader = classLoader;
+    }
+}
diff --git a/test-runner/android/test/ComparisonFailure.java b/test-runner/android/test/ComparisonFailure.java
new file mode 100644
index 0000000..e7e9698
--- /dev/null
+++ b/test-runner/android/test/ComparisonFailure.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package android.test;
+
+/**
+ * Thrown when an assert equals for Strings failed.
+ * 
+ * Note:  Most users of this class should simply use junit.framework.ComparisonFailure,
+ * which provides the same functionality at a lighter weight.
+ */
+public class ComparisonFailure extends AssertionFailedError {
+    private junit.framework.ComparisonFailure mComparison;
+
+    public ComparisonFailure(String message, String expected, String actual) {
+        mComparison = new junit.framework.ComparisonFailure(message, expected, actual);
+    }
+
+    public String getMessage() {
+        return mComparison.getMessage();
+    }
+}
diff --git a/test-runner/android/test/DatabaseTestUtils.java b/test-runner/android/test/DatabaseTestUtils.java
new file mode 100644
index 0000000..23e0aba
--- /dev/null
+++ b/test-runner/android/test/DatabaseTestUtils.java
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+package android.test;
+
+import com.google.android.collect.Sets;
+
+import android.database.sqlite.SQLiteDatabase;
+import android.database.Cursor;
+
+import java.util.Set;
+
+/**
+ * A collection of utilities for writing unit tests for database code.
+ * @hide pending API council approval
+ */
+public class DatabaseTestUtils {
+
+    /**
+     * Compares the schema of two databases and asserts that they are equal.
+     * @param expectedDb the db that is known to have the correct schema
+     * @param db the db whose schema should be checked
+     */
+    public static void assertSchemaEquals(SQLiteDatabase expectedDb, SQLiteDatabase db) {
+        Set<String> expectedSchema = getSchemaSet(expectedDb);
+        Set<String> schema = getSchemaSet(db);
+        MoreAsserts.assertEquals(expectedSchema, schema);
+    }
+
+    private static Set<String> getSchemaSet(SQLiteDatabase db) {
+        Set<String> schemaSet = Sets.newHashSet();
+
+        Cursor entityCursor = db.rawQuery("SELECT sql FROM sqlite_master", null);
+        try {
+            while (entityCursor.moveToNext()) {
+                String sql = entityCursor.getString(0);
+                schemaSet.add(sql);
+            }
+        } finally {
+            entityCursor.close();
+        }
+        return schemaSet;
+    }
+}
diff --git a/test-runner/android/test/InstrumentationCoreTestRunner.java b/test-runner/android/test/InstrumentationCoreTestRunner.java
new file mode 100644
index 0000000..6b1a4e4
--- /dev/null
+++ b/test-runner/android/test/InstrumentationCoreTestRunner.java
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+package android.test;
+
+import java.io.File;
+
+import android.os.Bundle;
+
+/**
+ * This test runner extends the default InstrumentationTestRunner. It overrides
+ * the {@code onCreate(Bundle)} method and sets the system properties necessary
+ * for many core tests to run. This is needed because there are some core tests
+ * that need writing access to the filesystem.
+ *
+ * @hide
+ */
+public class InstrumentationCoreTestRunner extends InstrumentationTestRunner {
+
+    @Override
+    public void onCreate(Bundle arguments) {
+        super.onCreate(arguments);
+        
+        File cacheDir = getTargetContext().getCacheDir();
+
+        System.setProperty("user.language", "en");
+        System.setProperty("user.region", "US");
+        System.setProperty("java.io.tmpdir", cacheDir.getAbsolutePath());
+    }
+}
diff --git a/test-runner/android/test/InstrumentationTestRunner.java b/test-runner/android/test/InstrumentationTestRunner.java
new file mode 100644
index 0000000..f038612
--- /dev/null
+++ b/test-runner/android/test/InstrumentationTestRunner.java
@@ -0,0 +1,672 @@
+/*
+ * Copyright (C) 2007 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.test;
+
+import static android.test.suitebuilder.TestPredicates.REJECT_PERFORMANCE;
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.os.Bundle;
+import android.os.Debug;
+import android.os.Looper;
+import android.test.suitebuilder.TestMethod;
+import android.test.suitebuilder.TestPredicates;
+import android.test.suitebuilder.TestSuiteBuilder;
+import android.util.Log;
+
+import com.android.internal.util.Predicate;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestListener;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+import junit.runner.BaseTestRunner;
+import junit.textui.ResultPrinter;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.PrintStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+
+/**
+ * An {@link Instrumentation} that runs various types of {@link junit.framework.TestCase}s against
+ * an Android package (application). Typical usage:
+ * <ol>
+ * <li>Write {@link junit.framework.TestCase}s that perform unit, functional, or performance tests
+ * against the classes in your package.  Typically these are subclassed from:
+ *   <ul><li>{@link android.test.ActivityInstrumentationTestCase}</li>
+ *   <li>{@link android.test.ActivityUnitTestCase}</li>
+ *   <li>{@link android.test.AndroidTestCase}</li>
+ *   <li>{@link android.test.ApplicationTestCase}</li>
+ *   <li>{@link android.test.InstrumentationTestCase}</li>
+ *   <li>{@link android.test.ProviderTestCase}</li>
+ *   <li>{@link android.test.ServiceTestCase}</li>
+ *   <li>{@link android.test.SingleLaunchActivityTestCase}</li></ul>
+ *   <li>In an appropriate AndroidManifest.xml, define the this instrumentation with
+ * the appropriate android:targetPackage set.
+ * <li>Run the instrumentation using "adb shell am instrument -w",
+ * with no optional arguments, to run all tests (except performance tests).
+ * <li>Run the instrumentation using "adb shell am instrument -w",
+ * with the argument '-e func true' to run all functional tests. These are tests that derive from
+ * {@link android.test.InstrumentationTestCase}.
+ * <li>Run the instrumentation using "adb shell am instrument -w",
+ * with the argument '-e unit true' to run all unit tests. These are tests that <i>do not</i>derive
+ * from {@link android.test.InstrumentationTestCase} (and are not performance tests).
+ * <li>Run the instrumentation using "adb shell am instrument -w",
+ * with the argument '-e class' set to run an individual {@link junit.framework.TestCase}.
+ * </ol>
+ * <p/>
+ * <b>Running all tests:</b> adb shell am instrument -w
+ * com.android.foo/android.test.InstrumentationTestRunner
+ * <p/>
+ * <b>Running all small tests:</b> adb shell am instrument -w
+ * -e size small
+ * com.android.foo/android.test.InstrumentationTestRunner
+ * <p/>
+ * <b>Running all medium tests:</b> adb shell am instrument -w
+ * -e size medium
+ * com.android.foo/android.test.InstrumentationTestRunner
+ * <p/>
+ * <b>Running all large tests:</b> adb shell am instrument -w
+ * -e size large
+ * com.android.foo/android.test.InstrumentationTestRunner
+ * <p/>
+ * <b>Running a single testcase:</b> adb shell am instrument -w
+ * -e class com.android.foo.FooTest
+ * com.android.foo/android.test.InstrumentationTestRunner
+ * <p/>
+ * <b>Running a single test:</b> adb shell am instrument -w
+ * -e class com.android.foo.FooTest#testFoo
+ * com.android.foo/android.test.InstrumentationTestRunner
+ * <p/>
+ * <b>Running multiple tests:</b> adb shell am instrument -w
+ * -e class com.android.foo.FooTest,com.android.foo.TooTest
+ * com.android.foo/android.test.InstrumentationTestRunner
+ * <p/>
+ * <b>Including performance tests:</b> adb shell am instrument -w
+ * -e perf true
+ * com.android.foo/android.test.InstrumentationTestRunner
+ * <p/>
+ * <b>To debug your tests, set a break point in your code and pass:</b>
+ * -e debug true
+ * <p/>
+ * <b>To run in 'log only' mode</b>
+ * -e log true
+ * This option will load and iterate through all test classes and methods, but will bypass actual 
+ * test execution. Useful for quickly obtaining info on the tests to be executed by an 
+ * instrumentation command.
+ * <p/>
+ * <b>To generate EMMA code coverage:</b>
+ * -e coverage true
+ * Note: this requires an emma instrumented build. By default, the code coverage results file 
+ * will be saved as /sdcard/coverage.ec, unless overridden by coverageFile flag (see below)
+ * <p/>
+ * <b> To specify EMMA code coverage results file path:</b>
+ * -e coverageFile /sdcard/myFile.ec
+ * <br/>
+ * in addition to the other arguments.
+ */
+
+/* (not JavaDoc)
+ * Although not necessary in most case, another way to use this class is to extend it and have the
+ * derived class return
+ * the desired test suite from the {@link #getTestSuite()} method. The test suite returned from this
+ * method will be used if no target class is defined in the meta-data or command line argument
+ * parameters. If a derived class is used it needs to be added as an instrumentation to the
+ * AndroidManifest.xml and the command to run it would look like:
+ * <p/>
+ * adb shell am instrument -w com.android.foo/<i>com.android.FooInstrumentationTestRunner</i>
+ * <p/>
+ * Where <i>com.android.FooInstrumentationTestRunner</i> is the derived class.
+ *
+ * This model is used by many existing app tests, but can probably be deprecated.
+ */
+public class InstrumentationTestRunner extends Instrumentation implements TestSuiteProvider {
+
+    /** @hide */
+    public static final String ARGUMENT_TEST_CLASS = "class";
+    /** @hide */
+    public static final String ARGUMENT_TEST_PACKAGE = "package";
+    /** @hide */
+    public static final String ARGUMENT_TEST_SIZE_PREDICATE = "size";
+    /** @hide */
+    public static final String ARGUMENT_INCLUDE_PERF = "perf";
+    /** @hide */
+    public static final String ARGUMENT_DELAY_MSEC = "delay_msec";
+
+    private static final String SMALL_SUITE = "small";
+    private static final String MEDIUM_SUITE = "medium";  
+    private static final String LARGE_SUITE = "large";
+    
+    private static final String ARGUMENT_LOG_ONLY = "log";
+
+   
+    /**
+     * This constant defines the maximum allowed runtime (in ms) for a test included in the "small" suite. 
+     * It is used to make an educated guess at what suite an unlabeled test belongs.
+     */
+    private static final float SMALL_SUITE_MAX_RUNTIME = 100;
+    
+    /**
+     * This constant defines the maximum allowed runtime (in ms) for a test included in the "medium" suite. 
+     * It is used to make an educated guess at what suite an unlabeled test belongs.
+     */
+    private static final float MEDIUM_SUITE_MAX_RUNTIME = 1000;
+    
+    /**
+     * The following keys are used in the status bundle to provide structured reports to 
+     * an IInstrumentationWatcher. 
+     */
+
+    /**
+     * This value, if stored with key {@link android.app.Instrumentation#REPORT_KEY_IDENTIFIER}, 
+     * identifies InstrumentationTestRunner as the source of the report.  This is sent with all
+     * status messages.
+     */
+    public static final String REPORT_VALUE_ID = "InstrumentationTestRunner";
+    /**
+     * If included in the status or final bundle sent to an IInstrumentationWatcher, this key 
+     * identifies the total number of tests that are being run.  This is sent with all status
+     * messages.
+     */
+    public static final String REPORT_KEY_NUM_TOTAL = "numtests";
+    /**
+     * If included in the status or final bundle sent to an IInstrumentationWatcher, this key 
+     * identifies the sequence number of the current test.  This is sent with any status message
+     * describing a specific test being started or completed.
+     */
+    public static final String REPORT_KEY_NUM_CURRENT = "current";
+    /**
+     * If included in the status or final bundle sent to an IInstrumentationWatcher, this key 
+     * identifies the name of the current test class.  This is sent with any status message
+     * describing a specific test being started or completed.
+     */
+    public static final String REPORT_KEY_NAME_CLASS = "class";
+    /**
+     * If included in the status or final bundle sent to an IInstrumentationWatcher, this key 
+     * identifies the name of the current test.  This is sent with any status message
+     * describing a specific test being started or completed.
+     */
+    public static final String REPORT_KEY_NAME_TEST = "test";
+    /**
+     * If included in the status or final bundle sent to an IInstrumentationWatcher, this key 
+     * reports the run time in seconds of the current test.
+     */
+    private static final String REPORT_KEY_RUN_TIME = "runtime";
+    /**
+     * If included in the status or final bundle sent to an IInstrumentationWatcher, this key 
+     * reports the guessed suite assignment for the current test.
+     */
+    private static final String REPORT_KEY_SUITE_ASSIGNMENT = "suiteassignment";
+    /**
+     * The test is starting.
+     */
+    public static final int REPORT_VALUE_RESULT_START = 1;
+    /**
+     * The test completed successfully.
+     */
+    public static final int REPORT_VALUE_RESULT_OK = 0;
+    /**
+     * The test completed with an error.
+     */
+    public static final int REPORT_VALUE_RESULT_ERROR = -1;
+    /**
+     * The test completed with a failure.
+     */
+    public static final int REPORT_VALUE_RESULT_FAILURE = -2;
+    /**
+     * If included in the status bundle sent to an IInstrumentationWatcher, this key 
+     * identifies a stack trace describing an error or failure.  This is sent with any status 
+     * message describing a specific test being completed.
+     */
+    public static final String REPORT_KEY_STACK = "stack";
+
+    private static final String DEFAULT_COVERAGE_FILE_PATH = "/sdcard/coverage.ec";
+    
+    private static final String LOG_TAG = "InstrumentationTestRunner";
+
+    private final Bundle mResults = new Bundle();
+    private AndroidTestRunner mTestRunner;
+    private boolean mDebug;
+    private boolean mJustCount;
+    private boolean mSuiteAssignmentMode;
+    private int mTestCount;
+    private String mPackageOfTests;
+    private boolean mCoverage;
+    private String mCoverageFilePath;
+    private int mDelayMsec;
+
+    @Override
+    public void onCreate(Bundle arguments) {
+        super.onCreate(arguments);
+
+        // Apk paths used to search for test classes when using TestSuiteBuilders.
+        String[] apkPaths =
+                {getTargetContext().getPackageCodePath(), getContext().getPackageCodePath()};
+        ClassPathPackageInfoSource.setApkPaths(apkPaths);
+
+        Predicate<TestMethod> testSizePredicate = null;
+        boolean includePerformance = false;
+        String testClassesArg = null;
+        boolean logOnly = false;
+
+        if (arguments != null) {
+            // Test class name passed as an argument should override any meta-data declaration.
+            testClassesArg = arguments.getString(ARGUMENT_TEST_CLASS);
+            mDebug = getBooleanArgument(arguments, "debug");
+            mJustCount = getBooleanArgument(arguments, "count");
+            mSuiteAssignmentMode = getBooleanArgument(arguments, "suiteAssignment");
+            mPackageOfTests = arguments.getString(ARGUMENT_TEST_PACKAGE);
+            testSizePredicate = getSizePredicateFromArg(
+                    arguments.getString(ARGUMENT_TEST_SIZE_PREDICATE));
+            includePerformance = getBooleanArgument(arguments, ARGUMENT_INCLUDE_PERF);
+            logOnly = getBooleanArgument(arguments, ARGUMENT_LOG_ONLY);
+            mCoverage = getBooleanArgument(arguments, "coverage");
+            mCoverageFilePath = arguments.getString("coverageFile");
+
+            try {
+                Object delay = arguments.get(ARGUMENT_DELAY_MSEC);  // Accept either string or int
+                if (delay != null) mDelayMsec = Integer.parseInt(delay.toString());
+            } catch (NumberFormatException e) {
+                Log.e(LOG_TAG, "Invalid delay_msec parameter", e);
+            }
+        }
+
+        TestSuiteBuilder testSuiteBuilder = new TestSuiteBuilder(getClass().getName(),
+                getTargetContext().getClassLoader());
+
+        if (testSizePredicate != null) {
+            testSuiteBuilder.addRequirements(testSizePredicate);
+        }
+        if (!includePerformance) {
+            testSuiteBuilder.addRequirements(REJECT_PERFORMANCE);
+        }
+
+        if (testClassesArg == null) {
+            TestSuite testSuite = null;
+            if (mPackageOfTests != null) {
+                testSuiteBuilder.includePackages(mPackageOfTests);
+            } else {
+                testSuite = getTestSuite();
+                testSuiteBuilder.addTestSuite(testSuite);
+            }
+
+            if (testSuite == null) {
+                testSuiteBuilder.includePackages(getTargetContext().getPackageName());
+            }
+        } else {
+            parseTestClasses(testClassesArg, testSuiteBuilder);
+        }
+
+        mTestRunner = getAndroidTestRunner();
+        mTestRunner.setContext(getTargetContext());
+        mTestRunner.setInstrumentaiton(this);
+        mTestRunner.setSkipExecution(logOnly);
+        mTestRunner.setTest(testSuiteBuilder.build());
+        mTestCount = mTestRunner.getTestCases().size();
+        if (mSuiteAssignmentMode) {
+            mTestRunner.addTestListener(new SuiteAssignmentPrinter());
+        } else {
+            mTestRunner.addTestListener(new TestPrinter("TestRunner", false));
+            mTestRunner.addTestListener(new WatcherResultPrinter(mTestCount));
+        }
+        start();
+    }
+
+    /**
+     * Parses and loads the specified set of test classes 
+     * @param testClassArg - comma-separated list of test classes and methods
+     * @param testSuiteBuilder - builder to add tests to
+     */
+    private void parseTestClasses(String testClassArg, TestSuiteBuilder testSuiteBuilder) {
+        String[] testClasses = testClassArg.split(",");
+        for (String testClass : testClasses) {
+            parseTestClass(testClass, testSuiteBuilder);
+        }
+    }
+
+    /**
+     * Parse and load the given test class and, optionally, method
+     * @param testClassName - full package name of test class and optionally method to add. Expected
+     *   format: com.android.TestClass#testMethod
+     * @param testSuiteBuilder - builder to add tests to
+     */
+    private void parseTestClass(String testClassName, TestSuiteBuilder testSuiteBuilder) {
+        int methodSeparatorIndex = testClassName.indexOf('#');
+        String testMethodName = null;
+
+        if (methodSeparatorIndex > 0) {
+            testMethodName = testClassName.substring(methodSeparatorIndex + 1);
+            testClassName = testClassName.substring(0, methodSeparatorIndex);
+        }
+        testSuiteBuilder.addTestClassByName(testClassName, testMethodName, 
+                getTargetContext());
+    }
+
+    protected AndroidTestRunner getAndroidTestRunner() {
+        return new AndroidTestRunner();
+    }
+
+    private boolean getBooleanArgument(Bundle arguments, String tag) {
+        String tagString = arguments.getString(tag);
+        return tagString != null && Boolean.parseBoolean(tagString);
+    }
+    
+    /*
+     * Returns the size predicate object, corresponding to the "size" argument value.
+     */
+    private Predicate<TestMethod> getSizePredicateFromArg(String sizeArg) {
+     
+        if (SMALL_SUITE.equals(sizeArg)) {
+            return TestPredicates.SELECT_SMALL;
+        } else if (MEDIUM_SUITE.equals(sizeArg)) {
+            return TestPredicates.SELECT_MEDIUM;
+        } else if (LARGE_SUITE.equals(sizeArg)) {
+            return TestPredicates.SELECT_LARGE;
+        } else {
+            return null;
+        }
+    }
+  
+    @Override
+    public void onStart() {
+        Looper.prepare();
+        
+        if (mJustCount) {
+            mResults.putString(Instrumentation.REPORT_KEY_IDENTIFIER, REPORT_VALUE_ID);
+            mResults.putInt(REPORT_KEY_NUM_TOTAL, mTestCount);
+            finish(Activity.RESULT_OK, mResults);
+        } else {
+            if (mDebug) {
+                Debug.waitForDebugger();
+            }
+    
+            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+            PrintStream writer = new PrintStream(byteArrayOutputStream);
+            try {
+                StringResultPrinter resultPrinter = new StringResultPrinter(writer);
+    
+                mTestRunner.addTestListener(resultPrinter);
+                
+                long startTime = System.currentTimeMillis();
+                mTestRunner.runTest();
+                long runTime = System.currentTimeMillis() - startTime;
+    
+                resultPrinter.print(mTestRunner.getTestResult(), runTime);
+            } finally {
+                mResults.putString(Instrumentation.REPORT_KEY_STREAMRESULT, 
+                        String.format("\nTest results for %s=%s", 
+                        mTestRunner.getTestClassName(), 
+                        byteArrayOutputStream.toString()));
+
+                if (mCoverage) {
+                    generateCoverageReport();
+                }
+                writer.close();
+                
+                finish(Activity.RESULT_OK, mResults);
+            }
+        }
+    }
+
+    public TestSuite getTestSuite() {
+        return getAllTests();
+    }
+
+    /**
+     * Override this to define all of the tests to run in your package.
+     */
+    public TestSuite getAllTests() {
+        return null;
+    }
+
+    /**
+     * Override this to provide access to the class loader of your package.
+     */
+    public ClassLoader getLoader() {
+        return null;
+    }
+    
+    private void generateCoverageReport() {
+        // use reflection to call emma dump coverage method, to avoid
+        // always statically compiling against emma jar
+        java.io.File coverageFile = new java.io.File(getCoverageFilePath());
+        try {
+            Class emmaRTClass = Class.forName("com.vladium.emma.rt.RT");
+            Method dumpCoverageMethod = emmaRTClass.getMethod("dumpCoverageData", 
+                    coverageFile.getClass(), boolean.class, boolean.class);
+            
+            dumpCoverageMethod.invoke(null, coverageFile, false, false);
+
+        } catch (ClassNotFoundException e) {
+            reportEmmaError("Is emma jar on classpath?", e);
+        } catch (SecurityException e) {
+            reportEmmaError(e);
+        } catch (NoSuchMethodException e) {
+            reportEmmaError(e);
+        } catch (IllegalArgumentException e) {
+            reportEmmaError(e);
+        } catch (IllegalAccessException e) {
+            reportEmmaError(e);
+        } catch (InvocationTargetException e) {
+            reportEmmaError(e);
+        }
+    }
+
+    private String getCoverageFilePath() {
+        if (mCoverageFilePath == null) {
+            return DEFAULT_COVERAGE_FILE_PATH;
+        }
+        else {
+            return mCoverageFilePath;
+        }
+    }
+
+    private void reportEmmaError(Exception e) {
+        reportEmmaError("", e); 
+    }
+
+    private void reportEmmaError(String hint, Exception e) {
+        String msg = "Failed to generate emma coverage. " + hint;
+        Log.e(LOG_TAG, msg, e);
+        mResults.putString(Instrumentation.REPORT_KEY_STREAMRESULT, "\nError: " + msg);
+    }
+
+    // TODO kill this, use status() and prettyprint model for better output
+    private class StringResultPrinter extends ResultPrinter {
+
+        public StringResultPrinter(PrintStream writer) {
+            super(writer);
+        }
+
+        synchronized void print(TestResult result, long runTime) {
+            printHeader(runTime);
+            printFooter(result);
+        }
+    }
+    
+    /**
+     * This class sends status reports back to the IInstrumentationWatcher about 
+     * which suite each test belongs.
+     */
+    private class SuiteAssignmentPrinter implements TestListener
+    {
+        
+        private Bundle mTestResult;
+        private long mStartTime;
+        private long mEndTime;
+        private boolean mTimingValid;
+        
+        public SuiteAssignmentPrinter() {
+        }
+        
+        /**
+         * send a status for the start of a each test, so long tests can be seen as "running"
+         */
+        public void startTest(Test test) {
+            mTimingValid = true;
+            mStartTime = System.currentTimeMillis(); 
+        }
+        
+        /**
+         * @see junit.framework.TestListener#addError(Test, Throwable)
+         */
+        public void addError(Test test, Throwable t) {
+            mTimingValid = false;
+        }
+
+        /**
+         * @see junit.framework.TestListener#addFailure(Test, AssertionFailedError)
+         */
+        public void addFailure(Test test, AssertionFailedError t) {
+            mTimingValid = false;
+        }
+
+        /**
+         * @see junit.framework.TestListener#endTest(Test)
+         */
+        public void endTest(Test test) {
+            float runTime;
+            String assignmentSuite;
+            mEndTime = System.currentTimeMillis();
+            mTestResult = new Bundle();
+
+            if (!mTimingValid || mStartTime < 0) {
+                assignmentSuite = "NA";
+                runTime = -1;
+            } else {
+                runTime = mEndTime - mStartTime;
+                if (runTime < SMALL_SUITE_MAX_RUNTIME 
+                        && !InstrumentationTestCase.class.isAssignableFrom(test.getClass())) {
+                    assignmentSuite = SMALL_SUITE;
+                } else if (runTime < MEDIUM_SUITE_MAX_RUNTIME) {
+                    assignmentSuite = MEDIUM_SUITE;
+                } else {
+                    assignmentSuite = LARGE_SUITE;
+                }
+            }
+            // Clear mStartTime so that we can verify that it gets set next time.
+            mStartTime = -1;
+
+            mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT, 
+                    test.getClass().getName() + "#" + ((TestCase) test).getName() 
+                    + "\nin " + assignmentSuite + " suite\nrunTime: "
+                    + String.valueOf(runTime) + "\n");
+            mTestResult.putFloat(REPORT_KEY_RUN_TIME, runTime);
+            mTestResult.putString(REPORT_KEY_SUITE_ASSIGNMENT, assignmentSuite);
+
+            sendStatus(0, mTestResult);
+        }
+    }
+    
+    /**
+     * This class sends status reports back to the IInstrumentationWatcher
+     */
+    private class WatcherResultPrinter implements TestListener
+    {
+        private final Bundle mResultTemplate;
+        Bundle mTestResult;
+        int mTestNum = 0;
+        int mTestResultCode = 0;
+        String mTestClass = null;
+        
+        public WatcherResultPrinter(int numTests) {
+            mResultTemplate = new Bundle();
+            mResultTemplate.putString(Instrumentation.REPORT_KEY_IDENTIFIER, REPORT_VALUE_ID);
+            mResultTemplate.putInt(REPORT_KEY_NUM_TOTAL, numTests);
+        }
+        
+        /**
+         * send a status for the start of a each test, so long tests can be seen as "running"
+         */
+        public void startTest(Test test) {
+            String testClass = test.getClass().getName();
+            mTestResult = new Bundle(mResultTemplate);
+            mTestResult.putString(REPORT_KEY_NAME_CLASS, testClass);
+            mTestResult.putString(REPORT_KEY_NAME_TEST, ((TestCase) test).getName());
+            mTestResult.putInt(REPORT_KEY_NUM_CURRENT, ++mTestNum);
+            // pretty printing
+            if (testClass != null && !testClass.equals(mTestClass)) {
+                mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT, 
+                        String.format("\n%s:", testClass));
+                mTestClass = testClass;
+            } else {
+                mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT, "");
+            }
+
+            // The delay_msec parameter is normally used to provide buffers of idle time
+            // for power measurement purposes.  To make sure there is a delay before and after
+            // every test in a suite, we delay *after* every test (see endTest below) and also
+            // delay *before* the first test.  So, delay test1 delay test2 delay.
+
+            try {
+                if (mTestNum == 1) Thread.sleep(mDelayMsec);
+            } catch (InterruptedException e) {
+                throw new IllegalStateException(e);
+            }
+
+            sendStatus(REPORT_VALUE_RESULT_START, mTestResult);
+            mTestResultCode = 0;
+        }
+        
+        /**
+         * @see junit.framework.TestListener#addError(Test, Throwable)
+         */
+        public void addError(Test test, Throwable t) {
+            mTestResult.putString(REPORT_KEY_STACK, BaseTestRunner.getFilteredTrace(t));
+            mTestResultCode = REPORT_VALUE_RESULT_ERROR;
+            // pretty printing
+            mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT, 
+                    String.format("\nError in %s:\n%s", 
+                            ((TestCase) test).getName(), BaseTestRunner.getFilteredTrace(t)));
+        }
+
+        /**
+         * @see junit.framework.TestListener#addFailure(Test, AssertionFailedError)
+         */
+        public void addFailure(Test test, AssertionFailedError t) {
+            mTestResult.putString(REPORT_KEY_STACK, BaseTestRunner.getFilteredTrace(t));
+            mTestResultCode = REPORT_VALUE_RESULT_FAILURE;
+            // pretty printing
+            mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT, 
+                    String.format("\nFailure in %s:\n%s", 
+                            ((TestCase) test).getName(), BaseTestRunner.getFilteredTrace(t)));
+        }
+
+        /**
+         * @see junit.framework.TestListener#endTest(Test)
+         */
+        public void endTest(Test test) {
+            if (mTestResultCode == 0) {
+                mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT, ".");
+            }
+            sendStatus(mTestResultCode, mTestResult);
+
+            try {  // Sleep after every test, if specified
+                Thread.sleep(mDelayMsec);
+            } catch (InterruptedException e) {
+                throw new IllegalStateException(e);
+            }
+        }
+
+        // TODO report the end of the cycle
+        // TODO report runtime for each test
+    }
+}
diff --git a/test-runner/android/test/InstrumentationUtils.java b/test-runner/android/test/InstrumentationUtils.java
new file mode 100644
index 0000000..4c59097
--- /dev/null
+++ b/test-runner/android/test/InstrumentationUtils.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2007 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.test;
+
+import java.lang.reflect.Field;
+
+/**
+ * 
+ * The InstrumentationUtils class has all the utility functions needed for
+ * instrumentation tests.
+ *
+ * {@hide} - Not currently used.
+ */
+public class InstrumentationUtils {
+    /**
+     * An utility function that returns the menu identifier for a particular
+     * menu item.
+     * 
+     * @param cls Class object of the class that handles the menu ite,.
+     * @param identifier Menu identifier.
+     * @return The integer corresponding to the menu item.
+     */
+    public static int getMenuIdentifier(Class cls, String identifier) {
+        int id = -1;
+        try {
+            Integer field = (Integer)cls.getDeclaredField(identifier).get(cls);   
+            id = field.intValue();
+        } catch (NoSuchFieldException e) {
+            e.printStackTrace();
+        } catch (IllegalAccessException e) {
+            e.printStackTrace();
+        }
+        return id;
+    }
+
+}
diff --git a/test-runner/android/test/IsolatedContext.java b/test-runner/android/test/IsolatedContext.java
new file mode 100644
index 0000000..2866666
--- /dev/null
+++ b/test-runner/android/test/IsolatedContext.java
@@ -0,0 +1,85 @@
+package android.test;
+
+import com.google.android.collect.Lists;
+
+import android.content.ContextWrapper;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.Context;
+import android.content.ServiceConnection;
+import android.content.BroadcastReceiver;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+
+import java.util.List;
+
+/**
+     * A mock context which prevents its users from talking to the rest of the device while
+ * stubbing enough methods to satify code that tries to talk to other packages.
+ */
+public class IsolatedContext extends ContextWrapper {
+
+    private ContentResolver mResolver;
+
+    private List<Intent> mBroadcastIntents = Lists.newArrayList();
+
+    public IsolatedContext(
+            ContentResolver resolver, Context targetContext) {
+        super(targetContext);
+        mResolver = resolver;
+    }
+
+    /** Returns the list of intents that were broadcast since the last call to this method. */
+    public List<Intent> getAndClearBroadcastIntents() {
+        List<Intent> intents = mBroadcastIntents;
+        mBroadcastIntents = Lists.newArrayList();
+        return intents;
+    }
+
+    @Override
+    public ContentResolver getContentResolver() {
+        // We need to return the real resolver so that MailEngine.makeRight can get to the
+        // subscribed feeds provider. TODO: mock out subscribed feeds too.
+        return mResolver;
+    }
+
+    @Override
+    public boolean bindService(Intent service, ServiceConnection conn, int flags) {
+        return false;
+    }
+
+    @Override
+    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+        return null;
+    }
+
+    @Override
+    public void sendBroadcast(Intent intent) {
+        mBroadcastIntents.add(intent);
+    }
+
+    @Override
+    public void sendOrderedBroadcast(Intent intent, String receiverPermission) {
+        mBroadcastIntents.add(intent);
+    }
+
+    @Override
+    public int checkUriPermission(
+            Uri uri, String readPermission, String writePermission, int pid,
+            int uid, int modeFlags) {
+        return PackageManager.PERMISSION_GRANTED;
+    }
+
+    @Override
+    public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags) {
+        return PackageManager.PERMISSION_GRANTED;
+    }
+
+    @Override
+    public Object getSystemService(String name) {
+        // No services exist in this context.
+        return null;
+    }
+
+}
diff --git a/test-runner/android/test/LaunchPerformanceBase.java b/test-runner/android/test/LaunchPerformanceBase.java
new file mode 100644
index 0000000..c324446
--- /dev/null
+++ b/test-runner/android/test/LaunchPerformanceBase.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2007 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.test;
+
+import android.app.Instrumentation;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.Debug;
+import android.os.Process;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+
+import java.util.ArrayList;
+
+
+/**
+ * Base class for all launch performance Instrumentation classes.
+ *
+ * @hide
+ */
+public class LaunchPerformanceBase extends Instrumentation {
+
+    public static final String LOG_TAG = "Launch Performance";
+
+    protected Bundle mResults;
+    protected Intent mIntent;
+
+    public LaunchPerformanceBase() {
+        mResults = new Bundle();
+        mIntent = new Intent(Intent.ACTION_MAIN);
+        mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        setAutomaticPerformanceSnapshots();
+    }
+   
+    /**
+     * Launches intent, and waits for idle before returning.
+     *
+     * @hide
+     */
+    protected void LaunchApp() {
+        startActivitySync(mIntent);
+        waitForIdleSync();
+    }
+}
diff --git a/test-runner/android/test/MoreAsserts.java b/test-runner/android/test/MoreAsserts.java
new file mode 100644
index 0000000..2e74644
--- /dev/null
+++ b/test-runner/android/test/MoreAsserts.java
@@ -0,0 +1,552 @@
+/*
+ * Copyright (C) 2007 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.test;
+
+import com.google.android.collect.Lists;
+import junit.framework.Assert;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.MatchResult;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Contains additional assertion methods not found in JUnit.
+ */
+public final class MoreAsserts {
+
+    private MoreAsserts() { }
+
+    /**
+     * Asserts that the class  {@code expected} is assignable from the object
+     * {@code actual}. This verifies {@code expected} is a parent class or a
+     * interface that {@code actual} implements.
+     */
+    public static void assertAssignableFrom(Class<?> expected, Object actual) {
+        assertAssignableFrom(expected, actual.getClass());
+    }
+
+    /**
+     * Asserts that class {@code expected} is assignable from the class
+     * {@code actual}. This verifies {@code expected} is a parent class or a
+     * interface that {@code actual} implements.
+     */
+    public static void assertAssignableFrom(Class<?> expected, Class<?> actual) {
+        Assert.assertTrue(
+                "Expected " + expected.getCanonicalName() +
+                        " to be assignable from actual class " + actual.getCanonicalName(),
+                expected.isAssignableFrom(actual));
+    }
+
+    /**
+     * Asserts that {@code actual} is not equal {@code unexpected}, according
+     * to both {@code ==} and {@link Object#equals}.
+     */
+    public static void assertNotEqual(
+            String message, Object unexpected, Object actual) {
+        if (equal(unexpected, actual)) {
+            failEqual(message, unexpected);
+        }
+    }
+
+    /**
+     * Variant of {@link #assertNotEqual(String,Object,Object)} using a
+     * generic message.
+     */
+    public static void assertNotEqual(Object unexpected, Object actual) {
+        assertNotEqual(null, unexpected, actual);
+    }
+
+    /**
+     * Asserts that array {@code actual} is the same size and every element equals
+     * those in array {@code expected}. On failure, message indicates specific
+     * element mismatch.
+     */
+    public static void assertEquals(
+            String message, byte[] expected, byte[] actual) {
+        if (expected.length != actual.length) {
+            failWrongLength(message, expected.length, actual.length);
+        }
+        for (int i = 0; i < expected.length; i++) {
+            if (expected[i] != actual[i]) {
+                failWrongElement(message, i, expected[i], actual[i]);
+            }
+        }
+    }
+
+    /**
+     * Asserts that array {@code actual} is the same size and every element equals
+     * those in array {@code expected}. On failure, message indicates specific
+     * element mismatch.
+     */
+    public static void assertEquals(byte[] expected, byte[] actual) {
+        assertEquals(null, expected, actual);
+    }
+
+    /**
+     * Asserts that array {@code actual} is the same size and every element equals
+     * those in array {@code expected}. On failure, message indicates first
+     * specific element mismatch.
+     */
+    public static void assertEquals(
+            String message, int[] expected, int[] actual) {
+        if (expected.length != actual.length) {
+            failWrongLength(message, expected.length, actual.length);
+        }
+        for (int i = 0; i < expected.length; i++) {
+            if (expected[i] != actual[i]) {
+                failWrongElement(message, i, expected[i], actual[i]);
+            }
+        }
+    }
+
+    /**
+     * Asserts that array {@code actual} is the same size and every element equals
+     * those in array {@code expected}. On failure, message indicates first
+     * specific element mismatch.
+     */
+    public static void assertEquals(int[] expected, int[] actual) {
+        assertEquals(null, expected, actual);
+    }
+
+    /**
+     * Asserts that array {@code actual} is the same size and every element equals
+     * those in array {@code expected}. On failure, message indicates first
+     * specific element mismatch.
+     */
+    public static void assertEquals(
+            String message, double[] expected, double[] actual) {
+        if (expected.length != actual.length) {
+            failWrongLength(message, expected.length, actual.length);
+        }
+        for (int i = 0; i < expected.length; i++) {
+            if (expected[i] != actual[i]) {
+                failWrongElement(message, i, expected[i], actual[i]);
+            }
+        }
+    }
+
+    /**
+     * Asserts that array {@code actual} is the same size and every element equals
+     * those in array {@code expected}. On failure, message indicates first
+     * specific element mismatch.
+     */
+    public static void assertEquals(double[] expected, double[] actual) {
+        assertEquals(null, expected, actual);
+    }
+
+    /**
+     * Asserts that array {@code actual} is the same size and every element
+     * is the same as those in array {@code expected}. Note that this uses
+     * {@code equals()} instead of {@code ==} to compare the objects.
+     * {@code null} will be considered equal to {code null} (unlike SQL).
+     * On failure, message indicates first specific element mismatch.
+     */
+    public static void assertEquals(
+            String message, Object[] expected, Object[] actual) {
+        if (expected.length != actual.length) {
+            failWrongLength(message, expected.length, actual.length);
+        }
+        for (int i = 0; i < expected.length; i++) {
+            Object exp = expected[i];
+            Object act = actual[i];
+            // The following borrowed from java.util.equals(Object[], Object[]).
+            if (!((exp==null) ? act==null : exp.equals(act))) {
+                failWrongElement(message, i, exp, act);
+            }
+        }
+    }
+
+    /**
+     * Asserts that array {@code actual} is the same size and every element
+     * is the same as those in array {@code expected}. Note that this uses
+     * {@code ==} instead of {@code equals()} to compare the objects.
+     * On failure, message indicates first specific element mismatch.
+     */
+    public static void assertEquals(Object[] expected, Object[] actual) {
+        assertEquals(null, expected, actual);
+    }
+
+    /** Asserts that two sets contain the same elements. */
+    public static void assertEquals(
+            String message, Set<? extends Object> expected, Set<? extends Object> actual) {
+        Set<Object> onlyInExpected = new HashSet<Object>(expected);
+        onlyInExpected.removeAll(actual);
+        Set<Object> onlyInActual = new HashSet<Object>(actual);
+        onlyInActual.removeAll(expected);
+        if (onlyInExpected.size() != 0 || onlyInActual.size() != 0) {
+            Set<Object> intersection = new HashSet<Object>(expected);
+            intersection.retainAll(actual);
+            failWithMessage(
+                    message,
+                    "Sets do not match.\nOnly in expected: " + onlyInExpected
+                    + "\nOnly in actual: " + onlyInActual
+                    + "\nIntersection: " + intersection);
+        }
+    }
+
+    /** Asserts that two sets contain the same elements. */
+    public static void assertEquals(Set<? extends Object> expected, Set<? extends Object> actual) {
+        assertEquals(null, expected, actual);
+    }
+
+    /**
+     * Asserts that {@code expectedRegex} exactly matches {@code actual} and
+     * fails with {@code message} if it does not.  The MatchResult is returned
+     * in case the test needs access to any captured groups.  Note that you can
+     * also use this for a literal string, by wrapping your expected string in
+     * {@link Pattern#quote}.
+     */
+    public static MatchResult assertMatchesRegex(
+            String message, String expectedRegex, String actual) {
+        if (actual == null) {
+            failNotMatches(message, expectedRegex, actual);
+        }
+        Matcher matcher = getMatcher(expectedRegex, actual);
+        if (!matcher.matches()) {
+            failNotMatches(message, expectedRegex, actual);
+        }
+        return matcher;
+    }
+
+    /**
+     * Variant of {@link #assertMatchesRegex(String,String,String)} using a
+     * generic message.
+     */
+    public static MatchResult assertMatchesRegex(
+            String expectedRegex, String actual) {
+        return assertMatchesRegex(null, expectedRegex, actual);
+    }
+
+    /**
+     * Asserts that {@code expectedRegex} matches any substring of {@code actual}
+     * and fails with {@code message} if it does not.  The Matcher is returned in
+     * case the test needs access to any captured groups.  Note that you can also
+     * use this for a literal string, by wrapping your expected string in
+     * {@link Pattern#quote}.
+     */
+    public static MatchResult assertContainsRegex(
+            String message, String expectedRegex, String actual) {
+        if (actual == null) {
+            failNotContains(message, expectedRegex, actual);
+        }
+        Matcher matcher = getMatcher(expectedRegex, actual);
+        if (!matcher.find()) {
+            failNotContains(message, expectedRegex, actual);
+        }
+        return matcher;
+    }
+
+    /**
+     * Variant of {@link #assertContainsRegex(String,String,String)} using a
+     * generic message.
+     */
+    public static MatchResult assertContainsRegex(
+            String expectedRegex, String actual) {
+        return assertContainsRegex(null, expectedRegex, actual);
+    }
+
+    /**
+     * Asserts that {@code expectedRegex} does not exactly match {@code actual},
+     * and fails with {@code message} if it does. Note that you can also use
+     * this for a literal string, by wrapping your expected string in
+     * {@link Pattern#quote}.
+     */
+    public static void assertNotMatchesRegex(
+            String message, String expectedRegex, String actual) {
+        Matcher matcher = getMatcher(expectedRegex, actual);
+        if (matcher.matches()) {
+            failMatch(message, expectedRegex, actual);
+        }
+    }
+
+    /**
+     * Variant of {@link #assertNotMatchesRegex(String,String,String)} using a
+     * generic message.
+     */
+    public static void assertNotMatchesRegex(
+            String expectedRegex, String actual) {
+        assertNotMatchesRegex(null, expectedRegex, actual);
+    }
+
+    /**
+     * Asserts that {@code expectedRegex} does not match any substring of
+     * {@code actual}, and fails with {@code message} if it does.  Note that you
+     * can also use this for a literal string, by wrapping your expected string
+     * in {@link Pattern#quote}.
+     */
+    public static void assertNotContainsRegex(
+            String message, String expectedRegex, String actual) {
+        Matcher matcher = getMatcher(expectedRegex, actual);
+        if (matcher.find()) {
+            failContains(message, expectedRegex, actual);
+        }
+    }
+
+    /**
+     * Variant of {@link #assertNotContainsRegex(String,String,String)} using a
+     * generic message.
+     */
+    public static void assertNotContainsRegex(
+            String expectedRegex, String actual) {
+        assertNotContainsRegex(null, expectedRegex, actual);
+    }
+
+    /**
+     * Asserts that {@code actual} contains precisely the elements
+     * {@code expected}, and in the same order.
+     */
+    public static void assertContentsInOrder(
+            String message, Iterable<?> actual, Object... expected) {
+        Assert.assertEquals(message,
+                Arrays.asList(expected), Lists.newArrayList(actual));
+    }
+
+    /**
+     * Variant of assertContentsInOrder(String, Iterable<?>, Object...)
+     * using a generic message.
+     */
+    public static void assertContentsInOrder(
+            Iterable<?> actual, Object... expected) {
+        assertContentsInOrder((String) null, actual, expected);
+    }
+
+    /**
+     * Asserts that {@code actual} contains precisely the elements
+     * {@code expected}, but in any order.
+     */
+    public static void assertContentsInAnyOrder(String message, Iterable<?> actual,
+            Object... expected) {
+        HashMap<Object, Object> expectedMap = new HashMap<Object, Object>(expected.length);
+        for (Object expectedObj : expected) {
+            expectedMap.put(expectedObj, expectedObj);
+        }
+
+        for (Object actualObj : actual) {
+            if (expectedMap.remove(actualObj) == null) {
+                failWithMessage(message, "Extra object in actual: (" + actualObj.toString() + ")");
+            }
+        }
+        
+        if (expectedMap.size() > 0) {
+            failWithMessage(message, "Extra objects in expected.");
+        }
+    }
+
+    /**
+     * Variant of assertContentsInAnyOrder(String, Iterable<?>, Object...)
+     * using a generic message.
+     */
+    public static void assertContentsInAnyOrder(Iterable<?> actual, Object... expected) {
+        assertContentsInAnyOrder((String)null, actual, expected);
+    }
+
+    /**
+     * Asserts that {@code iterable} is empty.
+     */
+    public static void assertEmpty(String message, Iterable<?> iterable) {
+        if (iterable.iterator().hasNext()) {
+            failNotEmpty(message, iterable.toString());
+        }
+    }
+
+    /**
+     * Variant of {@link #assertEmpty(String, Iterable)} using a
+     * generic message.
+     */
+    public static void assertEmpty(Iterable<?> iterable) {
+        assertEmpty(null, iterable);
+    }
+
+    /**
+     * Asserts that {@code map} is empty.
+     */
+    public static void assertEmpty(String message, Map<?,?> map) {
+        if (!map.isEmpty()) {
+            failNotEmpty(message, map.toString());
+        }
+    }
+
+    /**
+     * Variant of {@link #assertEmpty(String, Map)} using a generic
+     * message.
+     */
+    public  static void assertEmpty(Map<?,?> map) {
+        assertEmpty(null, map);
+    }
+
+    /**
+     * Asserts that {@code iterable} is not empty.
+     */
+    public static void assertNotEmpty(String message, Iterable<?> iterable) {
+        if (!iterable.iterator().hasNext()) {
+            failEmpty(message);
+        }
+    }
+
+    /**
+     * Variant of assertNotEmpty(String, Iterable<?>)
+     * using a generic message.
+     */
+    public static void assertNotEmpty(Iterable<?> iterable) {
+        assertNotEmpty(null, iterable);
+    }
+
+    /**
+     * Asserts that {@code map} is not empty.
+     */
+    public static void assertNotEmpty(String message, Map<?,?> map) {
+        if (map.isEmpty()) {
+            failEmpty(message);
+        }
+    }
+
+    /**
+     * Variant of {@link #assertNotEmpty(String, Map)} using a generic
+     * message.
+     */
+    public static void assertNotEmpty(Map<?,?> map) {
+        assertNotEmpty(null, map);
+    }
+
+    /**
+     * Utility for testing equals() and hashCode() results at once.
+     * Tests that lhs.equals(rhs) matches expectedResult, as well as
+     * rhs.equals(lhs).  Also tests that hashCode() return values are
+     * equal if expectedResult is true.  (hashCode() is not tested if
+     * expectedResult is false, as unequal objects can have equal hashCodes.)
+     *
+     * @param lhs An Object for which equals() and hashCode() are to be tested.
+     * @param rhs As lhs.
+     * @param expectedResult True if the objects should compare equal,
+     *   false if not.
+     */
+    public static void checkEqualsAndHashCodeMethods(
+            String message, Object lhs, Object rhs, boolean expectedResult) {
+
+        if ((lhs == null) && (rhs == null)) {
+            Assert.assertTrue(
+                    "Your check is dubious...why would you expect null != null?",
+                    expectedResult);
+            return;
+        }
+
+        if ((lhs == null) || (rhs == null)) {
+            Assert.assertFalse(
+                    "Your check is dubious...why would you expect an object "
+                            + "to be equal to null?", expectedResult);
+        }
+
+        if (lhs != null) {
+            Assert.assertEquals(message, expectedResult, lhs.equals(rhs));
+        }
+        if (rhs != null) {
+            Assert.assertEquals(message, expectedResult, rhs.equals(lhs));
+        }
+
+        if (expectedResult) {
+            String hashMessage =
+                    "hashCode() values for equal objects should be the same";
+            if (message != null) {
+                hashMessage += ": " + message;
+            }
+            Assert.assertTrue(hashMessage, lhs.hashCode() == rhs.hashCode());
+        }
+    }
+
+    /**
+     * Variant of
+     * checkEqualsAndHashCodeMethods(String,Object,Object,boolean...)}
+     * using a generic message.
+     */
+    public static void checkEqualsAndHashCodeMethods(Object lhs, Object rhs,
+            boolean expectedResult) {
+        checkEqualsAndHashCodeMethods((String) null, lhs, rhs, expectedResult);
+    }
+
+    private static Matcher getMatcher(String expectedRegex, String actual) {
+        Pattern pattern = Pattern.compile(expectedRegex);
+        return pattern.matcher(actual);
+    }
+
+    private static void failEqual(String message, Object unexpected) {
+        failWithMessage(message, "expected not to be:<" + unexpected + ">");
+    }
+
+    private static void failWrongLength(
+            String message, int expected, int actual) {
+        failWithMessage(message, "expected array length:<" + expected
+                + "> but was:<" + actual + '>');
+    }
+
+    private static void failWrongElement(
+            String message, int index, Object expected, Object actual) {
+        failWithMessage(message, "expected array element[" + index + "]:<"
+                + expected + "> but was:<" + actual + '>');
+    }
+
+    private static void failNotMatches(
+            String message, String expectedRegex, String actual) {
+        String actualDesc = (actual == null) ? "null" : ('<' + actual + '>');
+        failWithMessage(message, "expected to match regex:<" + expectedRegex
+                + "> but was:" + actualDesc);
+    }
+
+    private static void failNotContains(
+            String message, String expectedRegex, String actual) {
+        String actualDesc = (actual == null) ? "null" : ('<' + actual + '>');
+        failWithMessage(message, "expected to contain regex:<" + expectedRegex
+                + "> but was:" + actualDesc);
+    }
+
+    private static void failMatch(
+            String message, String expectedRegex, String actual) {
+        failWithMessage(message, "expected not to match regex:<" + expectedRegex
+                + "> but was:<" + actual + '>');
+    }
+
+    private static void failContains(
+            String message, String expectedRegex, String actual) {
+        failWithMessage(message, "expected not to contain regex:<" + expectedRegex
+                + "> but was:<" + actual + '>');
+    }
+
+    private static void failNotEmpty(
+            String message, String actual) {
+        failWithMessage(message, "expected to be empty, but contained: <"
+                + actual + ">");
+    }
+
+    private static void failEmpty(String message) {
+        failWithMessage(message, "expected not to be empty, but was");
+    }
+
+    private static void failWithMessage(String userMessage, String ourMessage) {
+        Assert.fail((userMessage == null)
+                ? ourMessage
+                : userMessage + ' ' + ourMessage);
+    }
+
+    private static boolean equal(Object a, Object b) {
+        return a == b || (a != null && a.equals(b));
+    }
+
+}
diff --git a/test-runner/android/test/NoExecTestResult.java b/test-runner/android/test/NoExecTestResult.java
new file mode 100755
index 0000000..1ee62c1
--- /dev/null
+++ b/test-runner/android/test/NoExecTestResult.java
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+package android.test;
+
+import junit.framework.TestCase;
+import junit.framework.TestResult;
+
+/**
+ * A benign test result that does no actually test execution, just runs 
+ * through the motions
+ * 
+ * {@hide} Not needed for SDK.
+ */
+class NoExecTestResult extends TestResult {
+
+    /**
+     * Override parent to just inform listeners of test,
+     * and skip test execution.
+     */
+    @Override
+    protected void run(final TestCase test) {
+        startTest(test);
+        endTest(test);
+    }
+
+}
diff --git a/test-runner/android/test/PackageInfoSources.java b/test-runner/android/test/PackageInfoSources.java
new file mode 100644
index 0000000..ef37449
--- /dev/null
+++ b/test-runner/android/test/PackageInfoSources.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package android.test;
+
+/**
+ * {@hide} Not needed for SDK.
+ */
+public class PackageInfoSources {
+
+    private static ClassPathPackageInfoSource classPathSource;
+
+    private PackageInfoSources() {
+    }
+
+    public static ClassPathPackageInfoSource forClassPath(ClassLoader classLoader) {
+        if (classPathSource == null) {
+            classPathSource = new ClassPathPackageInfoSource();
+            classPathSource.setClassLoader(classLoader);
+        }
+        return classPathSource;
+    }
+
+}
diff --git a/test-runner/android/test/PerformanceTestBase.java b/test-runner/android/test/PerformanceTestBase.java
new file mode 100644
index 0000000..93ac90c
--- /dev/null
+++ b/test-runner/android/test/PerformanceTestBase.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2007 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.test;
+
+import android.test.PerformanceTestCase;
+import junit.framework.TestCase;
+
+/**
+ * {@hide} Not needed for SDK.
+ */
+public abstract class PerformanceTestBase extends TestCase implements PerformanceTestCase {
+
+    public int startPerformance(PerformanceTestCase.Intermediates intermediates) {
+        return 0;
+    }
+
+    public boolean isPerformanceOnly() {
+        return true;
+    }
+
+    /*
+     * Temporary hack to get some things working again.
+     */
+    public void testRun() {
+        throw new RuntimeException("test implementation not provided");
+    }
+}
+
diff --git a/test-runner/android/test/ProviderTestCase.java b/test-runner/android/test/ProviderTestCase.java
new file mode 100644
index 0000000..445b4eb
--- /dev/null
+++ b/test-runner/android/test/ProviderTestCase.java
@@ -0,0 +1,87 @@
+package android.test;
+
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.test.mock.MockContext;
+import android.test.mock.MockContentResolver;
+import android.database.DatabaseUtils;
+
+/**
+ * If you would like to test a single content provider with an
+ * {@link InstrumentationTestCase}, this provides some of the boiler plate in {@link #setUp} and
+ * {@link #tearDown}.
+ *
+ * @deprecated this class extends InstrumentationTestCase but should extend AndroidTestCase. Use
+ * ProviderTestCase2, which corrects this problem, instead.
+ */
+public abstract class ProviderTestCase<T extends ContentProvider>
+       extends InstrumentationTestCase {
+
+    Class<T> mProviderClass;
+    String mProviderAuthority;
+
+    private IsolatedContext mProviderContext;
+    private MockContentResolver mResolver;
+
+    public ProviderTestCase(Class<T> providerClass, String providerAuthority) {
+        mProviderClass = providerClass;
+        mProviderAuthority = providerAuthority;
+    }
+
+    /**
+     * The content provider that will be set up for use in each test method.
+     */
+    private T mProvider;
+
+    public T getProvider() {
+        return mProvider;
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mResolver = new MockContentResolver();
+        final String filenamePrefix = "test.";
+        RenamingDelegatingContext targetContextWrapper = new RenamingDelegatingContext(
+                new MockContext(), // The context that most methods are delegated to
+                getInstrumentation().getTargetContext(), // The context that file methods are delegated to
+                filenamePrefix);
+        mProviderContext = new IsolatedContext(mResolver, targetContextWrapper);
+
+        mProvider = mProviderClass.newInstance();
+        mProvider.attachInfo(mProviderContext, null);
+        assertNotNull(mProvider);
+        mResolver.addProvider(mProviderAuthority, getProvider());
+    }
+
+    public MockContentResolver getMockContentResolver() {
+        return mResolver;
+    }
+
+    public IsolatedContext getMockContext() {
+        return mProviderContext;
+    }
+
+    public static <T extends ContentProvider> ContentResolver newResolverWithContentProviderFromSql(
+            Context targetContext, Class<T> providerClass, String authority,
+            String databaseName, int databaseVersion, String sql)
+            throws IllegalAccessException, InstantiationException {
+        final String filenamePrefix = "test.";
+        MockContentResolver resolver = new MockContentResolver();
+        RenamingDelegatingContext targetContextWrapper = new RenamingDelegatingContext(
+                new MockContext(), // The context that most methods are delegated to
+                targetContext, // The context that file methods are delegated to
+                filenamePrefix);
+        Context context = new IsolatedContext(
+                resolver, targetContextWrapper);
+        DatabaseUtils.createDbFromSqlStatements(context, databaseName, databaseVersion, sql);
+
+        T provider = providerClass.newInstance();
+        provider.attachInfo(context, null);
+        resolver.addProvider(authority, provider);
+
+        return resolver;
+    }
+}
diff --git a/test-runner/android/test/ProviderTestCase2.java b/test-runner/android/test/ProviderTestCase2.java
new file mode 100644
index 0000000..714b77b
--- /dev/null
+++ b/test-runner/android/test/ProviderTestCase2.java
@@ -0,0 +1,82 @@
+package android.test;
+
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.test.mock.MockContext;
+import android.test.mock.MockContentResolver;
+import android.database.DatabaseUtils;
+
+/**
+ * If you would like to test a single content provider with an
+ * {@link android.test.InstrumentationTestCase}, this provides some of the boiler plate in
+ * {@link #setUp} and {@link #tearDown}.
+ * @hide pending API council approval
+ */
+public abstract class ProviderTestCase2<T extends ContentProvider> extends AndroidTestCase {
+
+    Class<T> mProviderClass;
+    String mProviderAuthority;
+
+    private IsolatedContext mProviderContext;
+    private MockContentResolver mResolver;
+
+    public ProviderTestCase2(Class<T> providerClass, String providerAuthority) {
+        mProviderClass = providerClass;
+        mProviderAuthority = providerAuthority;
+    }
+
+    /**
+     * The content provider that will be set up for use in each test method.
+     */
+    private T mProvider;
+
+    public T getProvider() {
+        return mProvider;
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mResolver = new MockContentResolver();
+        final String filenamePrefix = "test.";
+        RenamingDelegatingContext targetContextWrapper = new RenamingDelegatingContext(
+                new MockContext(), // The context that most methods are delegated to
+                getContext(), // The context that file methods are delegated to
+                filenamePrefix);
+        mProviderContext = new IsolatedContext(mResolver, targetContextWrapper);
+
+        mProvider = mProviderClass.newInstance();
+        mProvider.attachInfo(mProviderContext, null);
+        assertNotNull(mProvider);
+        mResolver.addProvider(mProviderAuthority, getProvider());
+    }
+
+    public MockContentResolver getMockContentResolver() {
+        return mResolver;
+    }
+
+    public IsolatedContext getMockContext() {
+        return mProviderContext;
+    }
+
+    public static <T extends ContentProvider> ContentResolver newResolverWithContentProviderFromSql(
+            Context targetContext, String filenamePrefix, Class<T> providerClass, String authority,
+            String databaseName, int databaseVersion, String sql)
+            throws IllegalAccessException, InstantiationException {
+        MockContentResolver resolver = new MockContentResolver();
+        RenamingDelegatingContext targetContextWrapper = new RenamingDelegatingContext(
+                new MockContext(), // The context that most methods are delegated to
+                targetContext, // The context that file methods are delegated to
+                filenamePrefix);
+        Context context = new IsolatedContext(resolver, targetContextWrapper);
+        DatabaseUtils.createDbFromSqlStatements(context, databaseName, databaseVersion, sql);
+
+        T provider = providerClass.newInstance();
+        provider.attachInfo(context, null);
+        resolver.addProvider(authority, provider);
+
+        return resolver;
+    }
+}
\ No newline at end of file
diff --git a/test-runner/android/test/RenamingDelegatingContext.java b/test-runner/android/test/RenamingDelegatingContext.java
new file mode 100644
index 0000000..3f64340
--- /dev/null
+++ b/test-runner/android/test/RenamingDelegatingContext.java
@@ -0,0 +1,198 @@
+package android.test;
+
+import com.google.android.collect.Sets;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.ContentProvider;
+import android.database.sqlite.SQLiteDatabase;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.util.Set;
+
+/**
+ * This is a class which delegates to the given context, but performs database
+ * and file operations with a renamed database/file name (prefixes default
+ * names with a given prefix).
+ */
+public class RenamingDelegatingContext extends ContextWrapper {
+
+    private Context mFileContext;
+    private String mFilePrefix = null;
+
+    private Set<String> mDatabaseNames = Sets.newHashSet();
+    private Set<String> mFileNames = Sets.newHashSet();
+
+    public static <T extends ContentProvider> T providerWithRenamedContext(
+            Class<T> contentProvider, Context c, String filePrefix)
+            throws IllegalAccessException, InstantiationException {
+        return providerWithRenamedContext(contentProvider, c, filePrefix, false);
+    }
+
+    public static <T extends ContentProvider> T providerWithRenamedContext(
+            Class<T> contentProvider, Context c, String filePrefix,
+            boolean allowAccessToExistingFilesAndDbs)
+            throws IllegalAccessException, InstantiationException {
+        Class<T> mProviderClass = contentProvider;
+        T mProvider = mProviderClass.newInstance();
+        RenamingDelegatingContext mContext = new RenamingDelegatingContext(c, filePrefix);
+        if (allowAccessToExistingFilesAndDbs) {
+            mContext.makeExistingFilesAndDbsAccessible();
+        }
+        mProvider.attachInfo(mContext, null);
+        return mProvider;
+    }
+
+    /**
+     * Makes accessible all files and databases whose names match the filePrefix that was passed to
+     * the constructor. Normally only files and databases that were created through this context are
+     * accessible.
+     */
+    public void makeExistingFilesAndDbsAccessible() {
+        String[] databaseList = mFileContext.databaseList();
+        for (String diskName : databaseList) {
+            if (shouldDiskNameBeVisible(diskName)) {
+                mDatabaseNames.add(publicNameFromDiskName(diskName));
+            }
+        }
+        String[] fileList = mFileContext.fileList();
+        for (String diskName : fileList) {
+            if (shouldDiskNameBeVisible(diskName)) {
+                mFileNames.add(publicNameFromDiskName(diskName));
+            }
+        }
+    }
+
+    /**
+     * Returns if the given diskName starts with the given prefix or not.
+     * @param diskName name of the database/file.
+     */
+    boolean shouldDiskNameBeVisible(String diskName) {
+        return diskName.startsWith(mFilePrefix);
+    }
+
+    /**
+     * Returns the public name (everything following the prefix) of the given diskName.
+     * @param diskName name of the database/file.
+     */
+    String publicNameFromDiskName(String diskName) {
+        if (!shouldDiskNameBeVisible(diskName)) {
+            throw new IllegalArgumentException("disk file should not be visible: " + diskName);
+        }
+        return diskName.substring(mFilePrefix.length(), diskName.length());
+    }
+
+    /**
+     * @param context : the context that will be delagated.
+     * @param filePrefix : a prefix with which database and file names will be
+     * prefixed.
+     */
+    public RenamingDelegatingContext(Context context, String filePrefix) {
+        super(context);
+        mFileContext = context;
+        mFilePrefix = filePrefix;
+    }
+
+    /**
+     * @param context : the context that will be delagated.
+     * @param fileContext : the context that file and db methods will be delgated to
+     * @param filePrefix : a prefix with which database and file names will be
+     * prefixed.
+     */
+    public RenamingDelegatingContext(Context context, Context fileContext, String filePrefix) {
+        super(context);
+        mFileContext = fileContext;
+        mFilePrefix = filePrefix;
+    }
+
+    public String getDatabasePrefix() {
+        return mFilePrefix;
+    }
+
+    private String renamedFileName(String name) {
+        return mFilePrefix + name;
+    }
+
+    @Override
+    public SQLiteDatabase openOrCreateDatabase(String name,
+            int mode, SQLiteDatabase.CursorFactory factory) {
+        final String internalName = renamedFileName(name);
+        if (!mDatabaseNames.contains(name)) {
+            mDatabaseNames.add(name);
+            mFileContext.deleteDatabase(internalName);
+        }
+        return mFileContext.openOrCreateDatabase(internalName, mode, factory);
+    }
+
+    @Override
+    public boolean deleteDatabase(String name) {
+        if (mDatabaseNames.contains(name)) {
+            mDatabaseNames.remove(name);
+            return mFileContext.deleteDatabase(renamedFileName(name));
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public String[] databaseList() {
+        return mDatabaseNames.toArray(new String[]{});
+    }
+
+    @Override
+    public FileInputStream openFileInput(String name)
+            throws FileNotFoundException {
+        final String internalName = renamedFileName(name);
+        if (mFileNames.contains(name)) {
+            return mFileContext.openFileInput(internalName);
+        } else {
+            throw new FileNotFoundException(internalName);
+        }
+    }
+
+    @Override
+    public FileOutputStream openFileOutput(String name, int mode)
+            throws FileNotFoundException {
+        mFileNames.add(name);
+        return mFileContext.openFileOutput(renamedFileName(name), mode);
+    }
+
+    @Override
+    public File getFileStreamPath(String name) {
+        return mFileContext.getFileStreamPath(renamedFileName(name));
+    }
+
+    @Override
+    public boolean deleteFile(String name) {
+        if (mFileNames.contains(name)) {
+            mFileNames.remove(name);
+            return mFileContext.deleteFile(renamedFileName(name));
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public String[] fileList() {
+        return mFileNames.toArray(new String[]{});
+    }
+
+//    /**
+//     * Given an array of files returns only those whose names indicate that they belong to this
+//     * context.
+//     * @param allFiles the original list of files
+//     * @return the pruned list of files
+//     */
+//    private String[] prunedFileList(String[] allFiles) {
+//        List<String> files = Lists.newArrayList();
+//        for (String file : allFiles) {
+//            if (file.startsWith(mFilePrefix)) {
+//                files.add(file);
+//            }
+//        }
+//        return files.toArray(new String[]{});
+//    }
+}
\ No newline at end of file
diff --git a/test-runner/android/test/ServiceLocator.java b/test-runner/android/test/ServiceLocator.java
new file mode 100644
index 0000000..3324008
--- /dev/null
+++ b/test-runner/android/test/ServiceLocator.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2007 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.test;
+
+/**
+ * @hide - This is part of a framework that is under development and should not be used for
+ * active development.
+ */
+public class ServiceLocator {
+
+    private static TestBrowserController mTestBrowserController =
+            new TestBrowserControllerImpl();
+
+    public static TestBrowserController getTestBrowserController() {
+        return mTestBrowserController;
+    }
+
+    static void setTestBrowserController(TestBrowserController testBrowserController) {
+        mTestBrowserController = testBrowserController;
+    }
+}
diff --git a/test-runner/android/test/ServiceTestCase.java b/test-runner/android/test/ServiceTestCase.java
new file mode 100644
index 0000000..fcb9d55
--- /dev/null
+++ b/test-runner/android/test/ServiceTestCase.java
@@ -0,0 +1,280 @@
+/*
+ * 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.
+ */
+
+package android.test;
+
+import android.app.Application;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.test.mock.MockApplication;
+
+import java.lang.reflect.Field;
+import java.util.Random;
+
+/**
+ * This test case provides a framework in which you can test Service classes in
+ * a controlled environment.  It provides basic support for the lifecycle of a
+ * Service, and hooks by which you can inject various dependencies and control
+ * the environment in which your Service is tested.
+ *
+ * <p><b>Lifecycle Support.</b>
+ * Every Service is designed to be accessed within a specific sequence of
+ * calls.  <insert link to Service lifecycle doc here>. 
+ * In order to support the lifecycle of a Service, this test case will make the
+ * following calls at the following times.
+ *
+ * <ul><li>The test case will not call onCreate() until your test calls 
+ * {@link #startService} or {@link #bindService}.  This gives you a chance
+ * to set up or adjust any additional framework or test logic before
+ * onCreate().</li>
+ * <li>When your test calls {@link #startService} or {@link #bindService}
+ * the test case will call onCreate(), and then call the corresponding entry point in your service.
+ * It will record any parameters or other support values necessary to support the lifecycle.</li>
+ * <li>After your test completes, the test case {@link #tearDown} function is
+ * automatically called, and it will stop and destroy your service with the appropriate
+ * calls (depending on how your test invoked the service.)</li>
+ * </ul>
+ * 
+ * <p><b>Dependency Injection.</b>
+ * Every service has two inherent dependencies, the {@link android.content.Context Context} in
+ * which it runs, and the {@link android.app.Application Application} with which it is associated.
+ * This framework allows you to inject modified, mock, or isolated replacements for these 
+ * dependencies, and thus perform a true unit test.
+ * 
+ * <p>If simply run your tests as-is, your Service will be injected with a fully-functional
+ * Context, and a generic {@link android.test.mock.MockApplication MockApplication} object.
+ * You can create and inject alternatives to either of these by calling 
+ * {@link AndroidTestCase#setContext(Context) setContext()} or 
+ * {@link #setApplication setApplication()}.  You must do this <i>before</i> calling
+ * startService() or bindService().  The test framework provides a
+ * number of alternatives for Context, including {link android.test.mock.MockContext MockContext}, 
+ * {@link android.test.RenamingDelegatingContext RenamingDelegatingContext}, and 
+ * {@link android.content.ContextWrapper ContextWrapper}.
+ */
+public abstract class ServiceTestCase<T extends Service> extends AndroidTestCase {
+
+    Class<T> mServiceClass;
+
+    private Context mSystemContext;
+    private Application mApplication;
+
+    public ServiceTestCase(Class<T> serviceClass) {
+        mServiceClass = serviceClass;
+    }
+
+    private T mService;
+    private boolean mServiceAttached = false;
+    private boolean mServiceCreated = false;
+    private boolean mServiceStarted = false;
+    private boolean mServiceBound = false;
+    private Intent mServiceIntent = null;
+    private int mServiceId;
+
+    /**
+     * @return Returns the actual service under test.
+     */
+    public T getService() {
+        return mService;
+    }
+
+    /**
+     * This will do the work to instantiate the Service under test.  After this, your test 
+     * code must also start and stop the service.
+     */
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        
+        // get the real context, before the individual tests have a chance to muck with it
+        mSystemContext = getContext();
+
+    }
+    
+    /**
+     * Create the service under test and attach all injected dependencies (Context, Application) to
+     * it.  This will be called automatically by {@link #startService} or by {@link #bindService}.
+     * If you wish to call {@link AndroidTestCase#setContext(Context) setContext()} or 
+     * {@link #setApplication setApplication()}, you must do so  before calling this function.
+     */
+    protected void setupService() {
+        mService = null;
+        try {
+            mService = mServiceClass.newInstance();
+        } catch (Exception e) {
+            assertNotNull(mService);
+        }
+        if (getApplication() == null) {
+            setApplication(new MockApplication());
+        }
+        mService.attach(
+                getContext(),
+                null,               // ActivityThread not actually used in Service
+                mServiceClass.getName(),
+                null,               // token not needed when not talking with the activity manager
+                getApplication(),
+                null                // mocked services don't talk with the activity manager
+                );
+        
+        assertNotNull(mService);
+        
+        mServiceId = new Random().nextInt();
+        mServiceAttached = true;
+    }
+    
+    /**
+     * Start the service under test, in the same way as if it was started by
+     * {@link android.content.Context#startService Context.startService()}, providing the 
+     * arguments it supplied.  If you use this method to start the service, it will automatically
+     * be stopped by {@link #tearDown}.
+     *  
+     * @param intent The Intent as if supplied to {@link android.content.Context#startService}.
+     */
+    protected void startService(Intent intent) {
+        assertFalse(mServiceStarted);
+        assertFalse(mServiceBound);
+        
+        if (!mServiceAttached) {
+            setupService();
+        }
+        assertNotNull(mService);
+        
+        if (!mServiceCreated) {
+            mService.onCreate();
+            mServiceCreated = true;
+        }
+        mService.onStart(intent, mServiceId);
+        
+        mServiceStarted = true;
+    }
+    
+    /**
+     * Start the service under test, in the same way as if it was started by
+     * {@link android.content.Context#bindService Context.bindService()}, providing the 
+     * arguments it supplied.
+     *  
+     * Return the communication channel to the service.  May return null if 
+     * clients can not bind to the service.  The returned
+     * {@link android.os.IBinder} is usually for a complex interface
+     * that has been <a href="{@docRoot}guide/developing/tools/aidl.html">described using
+     * aidl</a>. 
+     * 
+     * Note:  In order to test with this interface, your service must implement a getService()
+     * method, as shown in samples.ApiDemos.app.LocalService.
+
+     * @param intent The Intent as if supplied to {@link android.content.Context#bindService}.
+     * 
+     * @return Return an IBinder for making further calls into the Service.
+     */
+    protected IBinder bindService(Intent intent) {
+        assertFalse(mServiceStarted);
+        assertFalse(mServiceBound);
+        
+        if (!mServiceAttached) {
+            setupService();
+        }
+        assertNotNull(mService);
+        
+        if (!mServiceCreated) {
+            mService.onCreate();
+            mServiceCreated = true;
+        }
+        // no extras are expected by unbind
+        mServiceIntent = intent.cloneFilter();
+        IBinder result = mService.onBind(intent);
+        
+        mServiceBound = true;
+        return result;
+    }
+    
+    /**
+     * This will make the necessary calls to stop (or unbind) the Service under test, and
+     * call onDestroy().  Ordinarily this will be called automatically (by {@link #tearDown}, but
+     * you can call it directly from your test in order to check for proper shutdown behaviors.
+     */
+    protected void shutdownService() {
+        if (mServiceStarted) {
+            mService.stopSelf();
+            mServiceStarted = false;
+        } else if (mServiceBound) {
+            mService.onUnbind(mServiceIntent);
+            mServiceBound = false;
+        }
+        if (mServiceCreated) {
+            mService.onDestroy();
+        }
+    }
+    
+    /**
+     * Shuts down the Service under test.  Also makes sure all resources are cleaned up and 
+     * garbage collected before moving on to the next
+     * test.  Subclasses that override this method should make sure they call super.tearDown()
+     * at the end of the overriding method.
+     * 
+     * @throws Exception
+     */
+    @Override
+    protected void tearDown() throws Exception {
+        shutdownService();
+        mService = null;
+
+        // Scrub out members - protects against memory leaks in the case where someone 
+        // creates a non-static inner class (thus referencing the test case) and gives it to
+        // someone else to hold onto
+        scrubClass(ServiceTestCase.class);
+
+        super.tearDown();
+    }
+    
+    /**
+     * Set the application for use during the test.  If your test does not call this function,
+     * a new {@link android.test.mock.MockApplication MockApplication} object will be generated.
+     * 
+     * @param application The Application object that will be injected into the Service under test.
+     */
+    public void setApplication(Application application) {
+        mApplication = application;
+    }
+
+    /**
+     * Return the Application object being used by the Service under test.
+     * 
+     * @return Returns the application object.
+     * 
+     * @see #setApplication
+     */
+    public Application getApplication() {
+        return mApplication;
+    }
+    
+    /**
+     * Return a real (not mocked or instrumented) system Context that can be used when generating
+     * Mock or other Context objects for your Service under test.
+     * 
+     * @return Returns a reference to a normal Context.
+     */
+    public Context getSystemContext() {
+        return mSystemContext;
+    }
+
+    public void testServiceTestCaseSetUpProperly() throws Exception {
+        setupService();
+        assertNotNull("service should be launched successfully", mService);
+    }
+}
diff --git a/test-runner/android/test/SimpleCache.java b/test-runner/android/test/SimpleCache.java
new file mode 100644
index 0000000..44424ec
--- /dev/null
+++ b/test-runner/android/test/SimpleCache.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package android.test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+abstract class SimpleCache<K, V> {
+    private Map<K, V> map = new HashMap<K, V>();
+
+    protected abstract V load(K key);
+
+    final V get(K key) {
+        if (map.containsKey(key)) {
+            return map.get(key);
+        }
+        V value = load(key);
+        map.put(key, value);
+        return value;
+    }
+}
diff --git a/test-runner/android/test/SingleLaunchActivityTestCase.java b/test-runner/android/test/SingleLaunchActivityTestCase.java
new file mode 100644
index 0000000..8d43b73
--- /dev/null
+++ b/test-runner/android/test/SingleLaunchActivityTestCase.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2007 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.test;
+
+import android.app.Activity;
+import android.view.IWindowManager;
+import android.os.ServiceManager;
+
+/**
+ * If you would like to test a single activity with an
+ * {@link android.test.InstrumentationTestCase}, this provides some of the boiler plate to
+ * launch and finish the activity in {@link #setUp} and {@link #tearDown}.
+ * 
+ * This launches the activity only once for the entire class instead of doing it 
+ * in every setup / teardown call.
+ */
+public abstract class SingleLaunchActivityTestCase<T extends Activity>
+        extends InstrumentationTestCase {
+    
+    String mPackage;
+    Class<T> mActivityClass;
+    private static int sTestCaseCounter = 0;
+    private static boolean sActivityLaunchedFlag = false;
+
+    /**
+     * @param pkg The package of the instrumentation.
+     * @param activityClass The activity to test.
+     */
+    public SingleLaunchActivityTestCase(String pkg, Class<T> activityClass) {
+        mPackage = pkg;
+        mActivityClass = activityClass;        
+        sTestCaseCounter ++;                
+    }
+    
+    /**
+     * The activity that will be set up for use in each test method.
+     */
+    private static Activity sActivity;
+
+    public T getActivity() {
+        return (T) sActivity;
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        // If it is the first test case, launch the activity.
+        if (!sActivityLaunchedFlag) {
+            // by default, not in touch mode
+            getInstrumentation().setInTouchMode(false);
+            sActivity = launchActivity(mPackage, mActivityClass, null);
+            sActivityLaunchedFlag = true;
+        }                        
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        // If it is the last test case, call finish on the activity.
+        sTestCaseCounter --;
+        if (sTestCaseCounter == 1) {
+            sActivity.finish();
+        }        
+        super.tearDown();
+    }
+
+    public void testActivityTestCaseSetUpProperly() throws Exception {
+        assertNotNull("activity should be launched successfully", sActivity);
+    }
+}
diff --git a/test-runner/android/test/SyncBaseInstrumentation.java b/test-runner/android/test/SyncBaseInstrumentation.java
new file mode 100644
index 0000000..c1d2507
--- /dev/null
+++ b/test-runner/android/test/SyncBaseInstrumentation.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2007 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.test;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.ContentValues;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.provider.Sync;
+import android.net.Uri;
+import java.util.Map;
+
+/**
+ * If you would like to test sync a single provider with an
+ * {@link InstrumentationTestCase}, this provides some of the boiler plate in {@link #setUp} and
+ * {@link #tearDown}.
+ */
+public class SyncBaseInstrumentation extends InstrumentationTestCase {
+    private Context mTargetContext;
+    ContentResolver mContentResolver;
+    private static final int MAX_TIME_FOR_SYNC_IN_MINS = 20;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mTargetContext = getInstrumentation().getTargetContext();
+        mContentResolver = mTargetContext.getContentResolver();
+    }
+
+    /**
+     * Syncs the specified provider.
+     * @throws Exception
+     */
+    protected void syncProvider(Uri uri, String account, String authority) throws Exception {
+        Bundle extras = new Bundle();
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_FORCE, true);
+        extras.putString(ContentResolver.SYNC_EXTRAS_ACCOUNT, account);
+
+        mContentResolver.startSync(uri, extras);
+        long startTimeInMillis = SystemClock.elapsedRealtime();
+        long endTimeInMillis = startTimeInMillis + MAX_TIME_FOR_SYNC_IN_MINS * 60000;
+
+        int counter = 0;
+        // Making sure race condition does not occur when en entry have been removed from pending
+        // and active tables and loaded in memory (therefore sync might be still in progress)
+        while (counter < 2) {
+            // Sleep for 1 second.
+            Thread.sleep(1000);
+            // Finish test if time to sync has exceeded max time.
+            if (SystemClock.elapsedRealtime() > endTimeInMillis) {
+                break;
+            }
+
+            if (isSyncActive(account, authority)) {
+                counter = 0;
+                continue;
+            }
+            counter++;
+        }
+    }
+
+    protected void cancelSyncsandDisableAutoSync() {
+        Sync.Settings.QueryMap mSyncSettings =
+                new Sync.Settings.QueryMap(mContentResolver, true, null);
+        mSyncSettings.setListenForNetworkTickles(false);
+        mContentResolver.cancelSync(null);
+        mSyncSettings.close();
+    }
+
+    /**
+     * This method tests if any sync is active or not. Sync is considered to be active if the
+     * entry is in either the Pending or Active tables.
+     * @return
+     */
+    private boolean isSyncActive(String account, String authority) {
+        Sync.Pending.QueryMap pendingQueryMap = null;
+        Sync.Active.QueryMap activeQueryMap = null;
+        try {
+            pendingQueryMap = new Sync.Pending.QueryMap(mContentResolver, false, null);
+            activeQueryMap = new Sync.Active.QueryMap(mContentResolver, false, null);
+
+            if (pendingQueryMap.isPending(account, authority)) {
+                return true;
+            }
+            if (isActiveInActiveQueryMap(activeQueryMap, account, authority)) {
+                return true;
+            }
+            return false;
+        } finally {
+            activeQueryMap.close();
+            pendingQueryMap.close();
+        }
+    }
+
+    private boolean isActiveInActiveQueryMap(Sync.Active.QueryMap activemap, String account,
+                                             String authority) {
+        Map<String, ContentValues> rows = activemap.getRows();
+        for (ContentValues values : rows.values()) {
+            if (values.getAsString("account").equals(account)
+                    && values.getAsString("authority").equals(authority)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/test-runner/android/test/TestBrowserActivity.java b/test-runner/android/test/TestBrowserActivity.java
new file mode 100644
index 0000000..ea5f91e
--- /dev/null
+++ b/test-runner/android/test/TestBrowserActivity.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2007 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.test;
+
+import com.android.internal.R;
+
+import android.app.ListActivity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import java.util.List;
+
+/**
+ * @hide - This is part of a framework that is under development and should not be used for
+ * active development.
+ */
+public abstract class TestBrowserActivity extends ListActivity
+        implements android.test.TestBrowserView, AdapterView.OnItemClickListener,
+        TestSuiteProvider {
+
+    private TestBrowserController mTestBrowserController;
+    public static final String BUNDLE_EXTRA_PACKAGE = "package";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        getListView().setOnItemClickListener(this);
+
+        mTestBrowserController = ServiceLocator.getTestBrowserController();
+        mTestBrowserController.setTargetPackageName(getPackageName());
+        mTestBrowserController.registerView(this);
+        mTestBrowserController.setTargetBrowserActivityClassName(this.getClass().getName());
+
+        // Apk paths used to search for test classes when using TestSuiteBuilders.
+        String[] apkPaths = {getPackageCodePath()};
+        ClassPathPackageInfoSource.setApkPaths(apkPaths);
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        TestSuite testSuite = getTestSuiteToBrowse();
+        mTestBrowserController.setTestSuite(testSuite);
+        
+        String name = testSuite.getName();
+        if (name != null) {
+            setTitle(name.substring(name.lastIndexOf(".") + 1));
+        }
+    }
+
+    /**
+     * Subclasses will override this method and return the TestSuite specific to their .apk.
+     * When this method is invoked due to an intent fired from
+     * {@link #onItemClick(android.widget.AdapterView, android.view.View, int, long)} then get the
+     * targeted TestSuite from the intent.
+     *
+     * @return testSuite to browse
+     */
+    @SuppressWarnings("unchecked")
+    private TestSuite getTestSuiteToBrowse() {
+        Intent intent = getIntent();
+        if (Intent.ACTION_RUN.equals(intent.getAction())) {
+            String testClassName = intent.getData().toString();
+
+            try {
+                Class<Test> testClass = (Class<Test>) getClassLoader().loadClass(testClassName);
+                return TestCaseUtil.createTestSuite(testClass);
+            } catch (ClassNotFoundException e) {
+                Log.e("TestBrowserActivity", "ClassNotFoundException for " + testClassName, e);
+                throw new RuntimeException(e);
+            } catch (IllegalAccessException e) {
+                Log.e("TestBrowserActivity", "IllegalAccessException for " + testClassName, e);
+                throw new RuntimeException(e);
+            } catch (InstantiationException e) {
+                Log.e("TestBrowserActivity", "InstantiationException for " + testClassName, e);
+                throw new RuntimeException(e);
+            }
+        } else {
+            // get test classes to browwes from subclass
+            return getTopTestSuite();
+        }
+
+    }
+
+    public TestSuite getTestSuite() {
+        return getTopTestSuite();
+    }
+
+    /**
+     * @return A TestSuite that should be run for a given application.
+     */
+    public abstract TestSuite getTopTestSuite();
+
+    public void onItemClick(AdapterView parent, View v, int position, long id) {
+        Intent intent = mTestBrowserController.getIntentForTestAt(position);
+        intent.putExtra(BUNDLE_EXTRA_PACKAGE, getPackageName());
+        startActivity(intent);
+    }
+
+    public void setTestNames(List<String> testNames) {
+        ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this,
+                R.layout.test_list_item, testNames);
+        setListAdapter(arrayAdapter);
+    }
+}
+
diff --git a/test-runner/android/test/TestBrowserController.java b/test-runner/android/test/TestBrowserController.java
new file mode 100644
index 0000000..044e39f
--- /dev/null
+++ b/test-runner/android/test/TestBrowserController.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2007 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.test;
+
+import android.content.Intent;
+import junit.framework.TestSuite;
+
+/**
+ * @hide - This is part of a framework that is under development and should not be used for
+ * active development.
+ */
+public interface TestBrowserController {
+    String BUNDLE_EXTRA_TEST_METHOD_NAME = "testMethodName";
+
+    Intent getIntentForTestAt(int position);
+
+    void setTestSuite(TestSuite testSuite);
+
+    void registerView(TestBrowserView testBrowserView);
+
+    void setTargetBrowserActivityClassName(String targetBrowserActivityClassName);
+
+    void setTargetPackageName(String targetPackageName);
+}
diff --git a/test-runner/android/test/TestBrowserControllerImpl.java b/test-runner/android/test/TestBrowserControllerImpl.java
new file mode 100644
index 0000000..b8f8975
--- /dev/null
+++ b/test-runner/android/test/TestBrowserControllerImpl.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2007 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.test;
+
+import android.content.Intent;
+import android.net.Uri;
+import com.google.android.collect.Lists;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import java.util.List;
+
+/**
+ * @hide - This is part of a framework that is under development and should not be used for
+ * active development.
+ */
+public class TestBrowserControllerImpl implements TestBrowserController {
+
+    static final String TEST_RUNNER_ACTIVITY_CLASS_NAME =
+           "com.android.testharness.TestRunnerActivity";
+
+    private TestSuite mTestSuite;
+    private TestBrowserView mTestBrowserView;
+    private static final int RUN_ALL_INDEX = 0;
+    private String mTargetBrowserActivityClassName;
+    private String mTargetPackageName;
+
+    public void setTargetPackageName(String targetPackageName) {
+        mTargetPackageName = targetPackageName;
+    }
+
+    public Intent getIntentForTestAt(int position) {
+        Intent intent = new Intent();
+        intent.setAction(Intent.ACTION_RUN);
+        // We must add the following two flags to make sure that we create a new activity when
+        // we browse nested test suites.
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+
+        String packageName = getDefaultPackageNameForTestRunner();
+        String className = "";
+        String testName = "";
+        if (shouldAllTestsBeRun(position)) {
+            testName = mTestSuite.getName();
+            className = TEST_RUNNER_ACTIVITY_CLASS_NAME;
+        } else {
+            Test test = TestCaseUtil.getTestAtIndex(mTestSuite, position - 1);
+            if (TestSuite.class.isAssignableFrom(test.getClass())) {
+                TestSuite testSuite = (TestSuite) test;
+                testName = testSuite.getName();
+                className = mTargetBrowserActivityClassName;
+                packageName = mTargetPackageName;
+            } else if (TestCase.class.isAssignableFrom(test.getClass())) {
+                TestCase testCase = (TestCase) test;
+                testName = testCase.getClass().getName();
+                className = TEST_RUNNER_ACTIVITY_CLASS_NAME;
+                String testMethodName = testCase.getName();
+                intent.putExtra(BUNDLE_EXTRA_TEST_METHOD_NAME, testMethodName);
+            }
+        }
+
+        intent.setClassName(packageName, className);
+        intent.setData(Uri.parse(testName));
+
+        return intent;
+    }
+
+    private String getDefaultPackageNameForTestRunner() {
+        return TEST_RUNNER_ACTIVITY_CLASS_NAME.substring(0,
+                TEST_RUNNER_ACTIVITY_CLASS_NAME.lastIndexOf("."));
+    }
+
+    private boolean shouldAllTestsBeRun(int position) {
+        return position == RUN_ALL_INDEX;
+    }
+
+    public void setTestSuite(TestSuite testSuite) {
+        mTestSuite = testSuite;
+
+        List<String> testCaseNames = Lists.newArrayList();
+        testCaseNames.add("Run All");
+        testCaseNames.addAll(TestCaseUtil.getTestCaseNames(testSuite, false));
+
+        mTestBrowserView.setTestNames(testCaseNames);
+    }
+
+    public void registerView(TestBrowserView testBrowserView) {
+        mTestBrowserView = testBrowserView;
+    }
+
+
+    public void setTargetBrowserActivityClassName(String targetBrowserActivityClassName) {
+        mTargetBrowserActivityClassName = targetBrowserActivityClassName;
+    }
+}
diff --git a/test-runner/android/test/TestBrowserView.java b/test-runner/android/test/TestBrowserView.java
new file mode 100644
index 0000000..4799f19
--- /dev/null
+++ b/test-runner/android/test/TestBrowserView.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2007 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.test;
+
+import java.util.List;
+
+/**
+ * @hide - This is part of a framework that is under development and should not be used for
+ * active development.
+ */
+public interface TestBrowserView {
+    void setTestNames(List<String> testNames);
+}
diff --git a/test-runner/android/test/TestCase.java b/test-runner/android/test/TestCase.java
new file mode 100644
index 0000000..5432ce8
--- /dev/null
+++ b/test-runner/android/test/TestCase.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2006 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.test;
+
+import android.content.Context;
+
+/**
+ * {@hide}
+ * More complex interface for test cases.
+ *
+ * <p>Just implementing Runnable is enough for many test cases.  If you
+ * have additional setup or teardown, this interface might be for you, 
+ * especially if you need to share it between different test cases, or your
+ * teardown code must execute regardless of whether your test passed.
+ *
+ * <p>See the android.test package documentation (click the more... link)
+ * for a full description
+ */
+
+@Deprecated
+public interface TestCase extends Runnable
+{
+    /**
+     * Called before run() is called.
+     */
+    public void setUp(Context context);
+
+    /**
+     * Called after run() is called, even if run() threw an exception, but
+     * not if setUp() threw an execption.
+     */
+    public void tearDown();
+}
+
diff --git a/test-runner/android/test/TestCaseUtil.java b/test-runner/android/test/TestCaseUtil.java
new file mode 100644
index 0000000..3ba9711
--- /dev/null
+++ b/test-runner/android/test/TestCaseUtil.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2007 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.test;
+
+import com.google.android.collect.Lists;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import junit.runner.BaseTestRunner;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @hide - This is part of a framework that is under development and should not be used for
+ * active development.
+ */
+public class TestCaseUtil {
+
+    private TestCaseUtil() {
+    }
+
+    @SuppressWarnings("unchecked")
+    public static List<String> getTestCaseNames(Test test, boolean flatten) {
+        List<Test> tests = (List<Test>) getTests(test, flatten);
+        List<String> testCaseNames = Lists.newArrayList();
+        for (Test aTest : tests) {
+            testCaseNames.add(getTestName(aTest));
+        }
+        return testCaseNames;
+    }
+
+    public static List<? extends Test> getTests(Test test, boolean flatten) {
+        return getTests(test, flatten, new HashSet<Class<?>>());
+    }
+
+    private static List<? extends Test> getTests(Test test, boolean flatten,
+            Set<Class<?>> seen) {
+        List<Test> testCases = Lists.newArrayList();
+        if (test != null) {
+
+            Test workingTest = null;
+            /*
+             * If we want to run a single TestCase method only, we must not
+             * invoke the suite() method, because we will run all test methods
+             * of the class then.
+             */
+            if (test instanceof TestCase &&
+                    ((TestCase)test).getName() == null) {
+                workingTest = invokeSuiteMethodIfPossible(test.getClass(), 
+                        seen);
+            }
+            if (workingTest == null) {
+                workingTest = test;
+            }
+
+            if (workingTest instanceof TestSuite) {
+                TestSuite testSuite = (TestSuite) workingTest;
+                Enumeration enumeration = testSuite.tests();
+                while (enumeration.hasMoreElements()) {
+                    Test childTest = (Test) enumeration.nextElement();
+                    if (flatten) {
+                        testCases.addAll(getTests(childTest, flatten, seen));
+                    } else {
+                        testCases.add(childTest);
+                    }
+                }
+            } else {
+                testCases.add(workingTest);
+            }
+        }
+        return testCases;
+    }
+
+    private static Test invokeSuiteMethodIfPossible(Class testClass,
+            Set<Class<?>> seen) {
+        try {
+            Method suiteMethod = testClass.getMethod(
+                    BaseTestRunner.SUITE_METHODNAME, new Class[0]);
+            /*
+             * Additional check necessary: If a TestCase contains a suite()
+             * method that returns a TestSuite including the TestCase itself,
+             * we need to stop the recursion. We use a set of classes to
+             * remember which classes' suite() methods were already invoked.
+             */
+            if (Modifier.isStatic(suiteMethod.getModifiers())
+                    && !seen.contains(testClass)) {
+                seen.add(testClass);
+                try {
+                    return (Test) suiteMethod.invoke(null, (Object[]) null);
+                } catch (InvocationTargetException e) {
+                    // do nothing
+                } catch (IllegalAccessException e) {
+                    // do nothing
+                }
+            }
+        } catch (NoSuchMethodException e) {
+            // do nothing
+        }
+        return null;
+    }
+
+    public static String getTestName(Test test) {
+        if (test instanceof TestCase) {
+            TestCase testCase = (TestCase) test;
+            return testCase.getName();
+        } else if (test instanceof TestSuite) {
+            TestSuite testSuite = (TestSuite) test;
+            String name = testSuite.getName();
+            if (name != null) {
+                int index = name.lastIndexOf(".");
+                if (index > -1) {
+                    return name.substring(index + 1);
+                } else {
+                    return name;
+                }
+            }
+        }
+        return "";
+    }
+
+    public static Test getTestAtIndex(TestSuite testSuite, int position) {
+        int index = 0;
+        Enumeration enumeration = testSuite.tests();
+        while (enumeration.hasMoreElements()) {
+            Test test = (Test) enumeration.nextElement();
+            if (index == position) {
+                return test;
+            }
+            index++;
+        }
+        return null;
+    }
+
+    public static TestSuite createTestSuite(Class<? extends Test> testClass)
+            throws InstantiationException, IllegalAccessException {
+
+        Test test = invokeSuiteMethodIfPossible(testClass, 
+                new HashSet<Class<?>>());
+        if (test == null) {
+            return new TestSuite(testClass);
+
+        } else if (TestCase.class.isAssignableFrom(test.getClass())) {
+            TestSuite testSuite = new TestSuite(test.getClass().getName());
+            testSuite.addTest(test);
+            return testSuite;
+        }
+
+        return (TestSuite) test;
+    }
+}
diff --git a/test-runner/android/test/TestListActivity.java b/test-runner/android/test/TestListActivity.java
new file mode 100644
index 0000000..a076a70
--- /dev/null
+++ b/test-runner/android/test/TestListActivity.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2007 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.test;
+
+import android.app.ListActivity;
+import android.content.Intent;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.CursorAdapter;
+import android.widget.ListView;
+import android.widget.SimpleCursorAdapter;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * Activity base class to use to implement your application's tests.
+ *
+ * <p>Implement the getTestSuite() method to return the name of your
+ * test suite class.
+ *
+ * <p>See the android.test package documentation (click the more... link)
+ * for a full description
+ * 
+ * {@hide} Not needed for SDK
+ */
+public abstract class TestListActivity extends ListActivity {
+    /** Supplied in the intent extras if we are running performance tests. */
+    public static final String PERFORMANCE_TESTS = "android.test.performance";
+
+    /** "Mode" group in the menu. */
+    static final int MODE_GROUP = Menu.FIRST;
+
+    /** Our suite */
+    String mSuite;
+
+    /** Our children tests */
+    String[] mTests;
+
+    /** which mode, REGRESSION, PERFORMANCE or PROFILING */
+    private int mMode = TestRunner.REGRESSION;
+
+    /** "Regression" menu item */
+    private MenuItem mRegressionItem;
+
+    /** "Performance" menu item */
+    private MenuItem mPerformanceItem;
+
+    /** "Profiling" menu item */
+    private MenuItem mProfilingItem;
+
+    private final Comparator<String> sComparator = new Comparator<String>() {
+        public final int compare(String a, String b) {
+            String s1 = makeCompareName(a);
+            String s2 = makeCompareName(b);
+            
+            return s1.compareToIgnoreCase(s2);
+        }
+    };
+
+    /**
+     * Constructor that doesn't do much.
+     */
+    public TestListActivity() {
+        super();
+    }
+
+    /**
+     * Subclasses should implement this to return the names of the classes
+     * of their tests.
+     *
+     * @return test suite class name
+     */
+    public abstract String getTestSuite();
+
+    /**
+     * Typical onCreate(Bundle icicle) implementation.
+     */
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        Intent intent = getIntent();
+
+        mMode = intent.getIntExtra(TestListActivity.PERFORMANCE_TESTS, mMode);
+
+
+        if (intent.getAction().equals(Intent.ACTION_MAIN)) {
+            // if we were called as MAIN, get the test suites,
+            mSuite = getTestSuite();
+        } else if (intent.getAction().equals(Intent.ACTION_RUN)) {
+            // We should have been provided a status channel.  Bail out and
+            // run the test instead.  This is how the TestHarness gets us
+            // loaded in our process for "Run All Tests."
+            Intent ntent = new Intent(Intent.ACTION_RUN,
+                    intent.getData() != null
+                            ? intent.getData()
+                            : Uri.parse(getTestSuite()));
+            ntent.setClassName("com.android.testharness",
+                    "com.android.testharness.RunTest");
+            ntent.putExtras(intent);
+            ntent.putExtra("package", getPackageName());
+            startActivity(ntent);
+            finish();
+            return;
+        } else if (intent.getAction().equals(Intent.ACTION_VIEW)) {
+            // otherwise use the one in the intent
+            mSuite = intent.getData() != null ? intent.getData().toString()
+                    : null;
+        }
+
+        String[] children = TestRunner.getChildren(this, mSuite);
+
+        Arrays.sort(children, sComparator);
+
+        int len = children.length;
+        mTests = new String[len];
+        System.arraycopy(children, 0, mTests, 0, len);
+
+        setTitle(TestRunner.getTitle(mSuite));
+
+        MatrixCursor cursor = new MatrixCursor(new String[] { "name", "_id" });
+        addTestRows(cursor);
+
+        CursorAdapter adapter = new SimpleCursorAdapter(
+                this,
+                com.android.internal.R.layout.simple_list_item_1,
+                cursor,
+                new String[] {"name"},
+                new int[] {com.android.internal.R.id.text1});
+        
+        setListAdapter(adapter);
+    }
+
+    private void addTestRows(MatrixCursor cursor) {
+        int id = 0;
+        cursor.newRow().add("Run All").add(id++);       
+        for (String test : mTests) {
+            String title = TestRunner.getTitle(test);
+            String prefix = TestRunner.isTestSuite(this, test)
+                    ? "Browse " : "Run ";
+
+            // I'd rather do this with an icon column, but I don't know how
+            cursor.newRow().add(prefix + title).add(id++);
+        }
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        super.onCreateOptionsMenu(menu);
+        mRegressionItem = menu.add(MODE_GROUP, -1, 0, "Regression Mode");
+        mPerformanceItem = menu.add(MODE_GROUP, -1, 0, "Performance Mode");
+        mProfilingItem = menu.add(MODE_GROUP, -1, 0, "Profiling Mode");
+        menu.setGroupCheckable(MODE_GROUP, true, true);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item == mRegressionItem) {
+            mMode = TestRunner.REGRESSION;
+        } else if (item == mPerformanceItem) {
+            mMode = TestRunner.PERFORMANCE;
+        } else if (item == mProfilingItem) {
+            mMode = TestRunner.PROFILING;
+        }
+        
+        return true;
+    }
+
+    @Override
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        super.onPrepareOptionsMenu(menu);
+        switch (mMode) {
+        case TestRunner.REGRESSION:
+            mRegressionItem.setChecked(true);
+            break;
+
+        case TestRunner.PERFORMANCE:
+            mPerformanceItem.setChecked(true);
+            break;
+
+        case TestRunner.PROFILING:
+            mProfilingItem.setChecked(true);
+            break;
+        }
+        return true;
+    }
+
+    @Override
+    protected void onListItemClick(ListView l, View v, int position, long id) {
+        Intent intent = new Intent();
+
+        if (position == 0) {
+            if (false) {
+                intent.setClassName("com.android.testharness",
+                        "com.android.testharness.RunAll");
+                intent.putExtra("tests", new String[]{mSuite});
+            } else {
+                intent.setClassName("com.android.testharness",
+                        "com.android.testharness.RunTest");
+                intent.setAction(Intent.ACTION_RUN);
+                intent.setData(Uri.parse(mSuite));
+            }
+        } else {
+            String test = mTests[position - 1];
+            if (TestRunner.isTestSuite(this, test)) {
+                intent.setClassName(getPackageName(), this.getClass().getName());
+                intent.setAction(Intent.ACTION_VIEW);
+            } else {
+                intent.setClassName("com.android.testharness",
+                        "com.android.testharness.RunTest");
+            }
+            intent.setData(Uri.parse(test));
+        }
+
+        intent.putExtra(PERFORMANCE_TESTS, mMode);
+        intent.putExtra("package", getPackageName());
+        startActivity(intent);
+    }
+
+    private String makeCompareName(String s) {
+        int index = s.lastIndexOf('.');
+        
+        if (index == -1) {
+            return s;
+        }
+        
+        return s.substring(index + 1);
+    }
+}
diff --git a/test-runner/android/test/TestLocationProvider.java b/test-runner/android/test/TestLocationProvider.java
new file mode 100644
index 0000000..00c1ce8
--- /dev/null
+++ b/test-runner/android/test/TestLocationProvider.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2007 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.test;
+
+
+import android.location.Criteria;
+import android.location.Location;
+import android.location.LocationProviderImpl;
+import android.os.Bundle;
+import android.os.SystemClock;
+
+/**
+ * @hide - This is part of a framework that is under development and should not be used for
+ * active development.
+ */
+public class TestLocationProvider extends LocationProviderImpl {
+
+    public static final String PROVIDER_NAME = "test";
+    public static final double LAT = 0;
+    public static final double LON = 1;
+    public static final double ALTITUDE = 10000;
+    public static final float SPEED = 10;
+    public static final float BEARING = 1;
+    public static final int STATUS = AVAILABLE;
+
+    private Location mLocation;
+    private boolean mEnabled;
+
+    public TestLocationProvider() {
+        super(PROVIDER_NAME);
+        mLocation = new Location(PROVIDER_NAME);
+        updateLocation();
+    }
+
+    //LocationProvider methods
+
+    @Override
+    public int getAccuracy() {
+        return Criteria.ACCURACY_COARSE;
+    }
+
+    @Override
+    public int getPowerRequirement() {
+        return Criteria.NO_REQUIREMENT;
+    }
+
+    @Override
+    public boolean hasMonetaryCost() {
+        return false;
+    }
+
+    @Override
+    public boolean requiresCell() {
+        return false;
+    }
+
+    @Override
+    public boolean requiresNetwork() {
+        return false;
+    }
+
+    @Override
+    public boolean requiresSatellite() {
+        return false;
+    }
+
+    @Override
+    public boolean supportsAltitude() {
+        return true;
+    }
+
+    @Override
+    public boolean supportsBearing() {
+        return true;
+    }
+
+    @Override
+    public boolean supportsSpeed() {
+        return true;
+    }
+
+    //LocationProviderImpl methods
+    @Override
+    public void disable() {
+        mEnabled = false;
+    }
+
+    @Override
+    public void enable() {
+        mEnabled = true;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return mEnabled;
+    }
+
+    @Override
+    public boolean getLocation(Location l) {
+        updateLocation();
+        l.set(mLocation);
+        return true;
+    }
+
+    @Override
+    public int getStatus(Bundle extras) {
+        return STATUS;
+    }
+
+    private void updateLocation() {
+        long time = SystemClock.uptimeMillis();
+        long multiplier = (time/5000)%500000;
+        mLocation.setLatitude(LAT*multiplier);
+        mLocation.setLongitude(LON*multiplier);
+        mLocation.setAltitude(ALTITUDE);
+        mLocation.setSpeed(SPEED);
+        mLocation.setBearing(BEARING*multiplier);
+
+        Bundle extras = new Bundle();
+        extras.putInt("extraTest", 24);
+        mLocation.setExtras(extras);
+        mLocation.setTime(time);
+    }
+
+}
diff --git a/test-runner/android/test/TestPrinter.java b/test-runner/android/test/TestPrinter.java
new file mode 100644
index 0000000..37bd721
--- /dev/null
+++ b/test-runner/android/test/TestPrinter.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2006 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.test;
+
+import android.util.Log;
+import junit.framework.Test;
+import junit.framework.TestListener;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Prints the test progress to stdout. Android includes a default
+ * implementation and calls these methods to print out test progress; you
+ * probably will not need to create or extend this class or call its methods manually.
+ * See the full {@link android.test} package description for information about
+ * getting test results.
+ * 
+ * {@hide} Not needed for 1.0 SDK.
+ */
+public class TestPrinter implements TestRunner.Listener, TestListener {
+
+    private String mTag;
+    private boolean mOnlyFailures;
+    private Set<String> mFailedTests = new HashSet<String>();
+
+
+    public TestPrinter(String tag, boolean onlyFailures) {
+        mTag = tag;
+        mOnlyFailures = onlyFailures;
+    }
+
+    public void started(String className) {
+        if (!mOnlyFailures) {
+            Log.i(mTag, "started: " + className);
+        }
+    }
+
+    public void finished(String className) {
+        if (!mOnlyFailures) {
+            Log.i(mTag, "finished: " + className);
+        }
+    }
+
+    public void performance(String className,
+            long itemTimeNS, int iterations,
+            List<TestRunner.IntermediateTime> intermediates) {
+        Log.i(mTag, "perf: " + className + " = " + itemTimeNS + "ns/op (done "
+                + iterations + " times)");
+        if (intermediates != null && intermediates.size() > 0) {
+            int N = intermediates.size();
+            for (int i = 0; i < N; i++) {
+                TestRunner.IntermediateTime time = intermediates.get(i);
+                Log.i(mTag, "  intermediate: " + time.name + " = "
+                        + time.timeInNS + "ns");
+            }
+        }
+    }
+
+    public void passed(String className) {
+        if (!mOnlyFailures) {
+            Log.i(mTag, "passed: " + className);
+        }
+    }
+
+    public void failed(String className, Throwable exception) {
+        Log.i(mTag, "failed: " + className);
+        Log.i(mTag, "----- begin exception -----");
+        Log.i(mTag, "", exception);
+        Log.i(mTag, "----- end exception -----");
+    }
+
+    private void failed(Test test, Throwable t) {
+        mFailedTests.add(test.toString());
+        failed(test.toString(), t);
+    }
+    
+    public void addError(Test test, Throwable t) {
+        failed(test, t);
+    }
+
+    public void addFailure(Test test, junit.framework.AssertionFailedError t) {
+        failed(test, t);
+    }
+
+    public void endTest(Test test) {
+        finished(test.toString());
+        if (!mFailedTests.contains(test.toString())) {
+            passed(test.toString());
+        }
+        mFailedTests.remove(test.toString());
+    }
+
+    public void startTest(Test test) {
+        started(test.toString());
+    }
+}
diff --git a/test-runner/android/test/TestRecorder.java b/test-runner/android/test/TestRecorder.java
new file mode 100644
index 0000000..7c368a0
--- /dev/null
+++ b/test-runner/android/test/TestRecorder.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2006 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.test;
+
+import android.content.ContentValues;
+import android.database.sqlite.SQLiteDatabase;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.test.TestRunner.IntermediateTime;
+import android.util.Log;
+import junit.framework.Test;
+import junit.framework.TestListener;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * {@hide} Not needed for 1.0 SDK.
+ */
+public class TestRecorder implements TestRunner.Listener, TestListener {
+    private static final int DATABASE_VERSION = 1;
+    private static SQLiteDatabase sDb;
+    private Set<String> mFailedTests = new HashSet<String>();
+
+    private static SQLiteDatabase getDatabase() {
+        if (sDb == null) {
+            File dir = new File(Environment.getDataDirectory(), "test_results");
+
+            /* TODO: add a DB version number and bootstrap/upgrade methods
+            * if the format of the table changes.
+            */
+            String dbName = "TestHarness.db";
+            File file = new File(dir, dbName);
+            sDb = SQLiteDatabase.openOrCreateDatabase(file.getPath(), null);
+
+            if (sDb.getVersion() == 0) {
+                int code = FileUtils.setPermissions(file.getPath(),
+                        FileUtils.S_IRUSR | FileUtils.S_IWUSR |
+                                FileUtils.S_IRGRP | FileUtils.S_IWGRP |
+                                FileUtils.S_IROTH | FileUtils.S_IWOTH, -1, -1);
+    
+                if (code != 0) {
+                    Log.w("TestRecorder",
+                            "Set permissions for " + file.getPath() + " returned = " + code);
+                }
+    
+                try {
+                    sDb.execSQL("CREATE TABLE IF NOT EXISTS tests (_id INT PRIMARY KEY," +
+                            "name TEXT," +
+                            "result TEXT," +
+                            "exception TEXT," +
+                            "started INTEGER," +
+                            "finished INTEGER," +
+                            "time INTEGER," +
+                            "iterations INTEGER," +
+                            "allocations INTEGER," +
+                            "parent INTEGER);");
+                    sDb.setVersion(DATABASE_VERSION);
+                } catch (Exception e) {
+                    Log.e("TestRecorder", "failed to create table 'tests'", e);
+                    sDb = null;
+                }
+            }
+        }
+
+        return sDb;
+    }
+
+    public TestRecorder() {
+    }
+
+    public void started(String className) {
+        ContentValues map = new ContentValues(2);
+        map.put("name", className);
+        map.put("started", System.currentTimeMillis());
+
+        // try to update the row first in case we've ran this test before.
+        int rowsAffected = getDatabase().update("tests", map, "name = '" + className + "'", null);
+
+        if (rowsAffected == 0) {
+            getDatabase().insert("tests", null, map);
+        }
+    }
+
+    public void finished(String className) {
+        ContentValues map = new ContentValues(1);
+        map.put("finished", System.currentTimeMillis());
+
+        getDatabase().update("tests", map, "name = '" + className + "'", null);
+    }
+
+    public void performance(String className, long itemTimeNS, int iterations, List<IntermediateTime> intermediates) {
+        ContentValues map = new ContentValues();
+        map.put("time", itemTimeNS);
+        map.put("iterations", iterations);
+
+        getDatabase().update("tests", map, "name = '" + className + "'", null);
+
+        if (intermediates != null && intermediates.size() > 0) {
+            int n = intermediates.size();
+            for (int i = 0; i < n; i++) {
+                TestRunner.IntermediateTime time = intermediates.get(i);
+
+                getDatabase().execSQL("INSERT INTO tests (name, time, parent) VALUES ('" +
+                        time.name + "', " + time.timeInNS + ", " +
+                        "(SELECT _id FROM tests WHERE name = '" + className + "'));");
+            }
+        }
+    }
+
+    public void passed(String className) {
+        ContentValues map = new ContentValues();
+        map.put("result", "passed");
+
+        getDatabase().update("tests", map, "name = '" + className + "'", null);
+    }
+
+    public void failed(String className, Throwable exception) {
+        StringWriter stringWriter = new StringWriter();
+        PrintWriter printWriter = new PrintWriter(stringWriter);
+        try {
+            exception.printStackTrace(printWriter);
+        } finally {
+            printWriter.close();
+        }
+        ContentValues map = new ContentValues();
+        map.put("result", "failed");
+        map.put("exception", stringWriter.toString());
+
+        getDatabase().update("tests", map, "name = '" + className + "'", null);
+    }
+
+    /**
+     * Reports a test case failure.
+     *
+     * @param className Name of the class/test.
+     * @param reason    Reason for failure.
+     */
+    public void failed(String className, String reason) {
+        ContentValues map = new ContentValues();
+        map.put("result", "failed");
+        // The reason is put as the exception.
+        map.put("exception", reason);
+        getDatabase().update("tests", map, "name = '" + className + "'", null);
+    }
+
+    public void addError(Test test, Throwable t) {
+        mFailedTests.add(test.toString());
+        failed(test.toString(), t);
+    }
+
+    public void addFailure(Test test, junit.framework.AssertionFailedError t) {
+        mFailedTests.add(test.toString());
+        failed(test.toString(), t.getMessage());
+    }
+
+    public void endTest(Test test) {
+        finished(test.toString());
+        if (!mFailedTests.contains(test.toString())) {
+            passed(test.toString());
+        }
+        mFailedTests.remove(test.toString());
+    }
+
+    public void startTest(Test test) {
+        started(test.toString());
+    }
+}
diff --git a/test-runner/android/test/TestRunner.java b/test-runner/android/test/TestRunner.java
new file mode 100644
index 0000000..efa2480
--- /dev/null
+++ b/test-runner/android/test/TestRunner.java
@@ -0,0 +1,723 @@
+/*
+ * Copyright (C) 2006 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.test;
+
+import android.content.Context;
+import android.util.Log;
+import android.os.Debug;
+import android.os.SystemClock;
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestSuite;
+import junit.framework.TestListener;
+import junit.framework.Test;
+import junit.framework.TestResult;
+import com.google.android.collect.Lists;
+
+/**
+ * Support class that actually runs a test. Android uses this class,
+ * and you probably will not need to instantiate, extend, or call this
+ * class yourself. See the full {@link android.test} package description
+ * to learn more about testing Android applications.
+ * 
+ * {@hide} Not needed for 1.0 SDK.
+ */
+public class TestRunner implements PerformanceTestCase.Intermediates {
+    public static final int REGRESSION = 0;
+    public static final int PERFORMANCE = 1;
+    public static final int PROFILING = 2;
+
+    public static final int CLEARSCREEN = 0;
+    private static final String TAG = "TestHarness";
+    private Context mContext;
+
+    private int mMode = REGRESSION;
+
+    private List<Listener> mListeners = Lists.newArrayList();
+    private int mPassed;
+    private int mFailed;
+
+    private int mInternalIterations;
+    private long mStartTime;
+    private long mEndTime;
+
+    private String mClassName;
+
+    List<IntermediateTime> mIntermediates = null;
+
+    private static Class mRunnableClass;
+    private static Class mJUnitClass;
+
+    static {
+        try {
+            mRunnableClass = Class.forName("java.lang.Runnable", false, null);
+            mJUnitClass = Class.forName("junit.framework.TestCase", false, null);
+        } catch (ClassNotFoundException ex) {
+            throw new RuntimeException("shouldn't happen", ex);
+        }
+    }
+
+    public class JunitTestSuite extends TestSuite implements TestListener {
+        boolean mError = false;
+
+        public JunitTestSuite() {
+            super();
+        }
+
+        public void run(TestResult result) {
+            result.addListener(this);
+            super.run(result);
+            result.removeListener(this);
+        }
+
+        /**
+         * Implemented method of the interface TestListener which will listen for the
+         * start of a test.
+         *
+         * @param test
+         */
+        public void startTest(Test test) {
+            started(test.toString());
+        }
+
+        /**
+         * Implemented method of the interface TestListener which will listen for the
+         * end of the test.
+         *
+         * @param test
+         */
+        public void endTest(Test test) {
+            finished(test.toString());
+            if (!mError) {
+                passed(test.toString());
+            }
+        }
+
+        /**
+         * Implemented method of the interface TestListener which will listen for an
+         * mError while running the test.
+         *
+         * @param test
+         */
+        public void addError(Test test, Throwable t) {
+            mError = true;
+            failed(test.toString(), t);
+        }
+
+        public void addFailure(Test test, junit.framework.AssertionFailedError t) {
+            mError = true;
+            failed(test.toString(), t);
+        }
+    }
+
+    /**
+     * Listener.performance() 'intermediates' argument is a list of these.
+     */
+    public static class IntermediateTime {
+        public IntermediateTime(String name, long timeInNS) {
+            this.name = name;
+            this.timeInNS = timeInNS;
+        }
+
+        public String name;
+        public long timeInNS;
+    }
+
+    /**
+     * Support class that receives status on test progress. You should not need to
+     * extend this interface yourself.
+     */
+    public interface Listener {
+        void started(String className);
+        void finished(String className);
+        void performance(String className,
+                long itemTimeNS, int iterations,
+                List<IntermediateTime> itermediates);
+        void passed(String className);
+        void failed(String className, Throwable execption);
+    }
+
+    public TestRunner(Context context) {
+        mContext = context;
+    }
+
+    public void addListener(Listener listener) {
+        mListeners.add(listener);
+    }
+
+    public void startProfiling() {
+        File file = new File("/tmp/trace");
+        file.mkdir();
+        String base = "/tmp/trace/" + mClassName + ".dmtrace";
+        Debug.startMethodTracing(base, 8 * 1024 * 1024);
+    }
+
+    public void finishProfiling() {
+        Debug.stopMethodTracing();
+    }
+
+    private void started(String className) {
+
+        int count = mListeners.size();
+        for (int i = 0; i < count; i++) {
+            mListeners.get(i).started(className);
+        }
+    }
+
+    private void finished(String className) {
+        int count = mListeners.size();
+        for (int i = 0; i < count; i++) {
+            mListeners.get(i).finished(className);
+        }
+    }
+
+    private void performance(String className,
+            long itemTimeNS,
+            int iterations,
+            List<IntermediateTime> intermediates) {
+        int count = mListeners.size();
+        for (int i = 0; i < count; i++) {
+            mListeners.get(i).performance(className,
+                    itemTimeNS,
+                    iterations,
+                    intermediates);
+        }
+    }
+
+    public void passed(String className) {
+        mPassed++;
+        int count = mListeners.size();
+        for (int i = 0; i < count; i++) {
+            mListeners.get(i).passed(className);
+        }
+    }
+
+    public void failed(String className, Throwable exception) {
+        mFailed++;
+        int count = mListeners.size();
+        for (int i = 0; i < count; i++) {
+            mListeners.get(i).failed(className, exception);
+        }
+    }
+
+    public int passedCount() {
+        return mPassed;
+    }
+
+    public int failedCount() {
+        return mFailed;
+    }
+
+    public void run(String[] classes) {
+        for (String cl : classes) {
+            run(cl);
+        }
+    }
+
+    public void setInternalIterations(int count) {
+        mInternalIterations = count;
+    }
+
+    public void startTiming(boolean realTime) {
+        if (realTime) {
+            mStartTime = System.currentTimeMillis();
+        } else {
+            mStartTime = SystemClock.currentThreadTimeMillis();
+        }
+    }
+
+    public void addIntermediate(String name) {
+        addIntermediate(name, (System.currentTimeMillis() - mStartTime) * 1000000);
+    }
+
+    public void addIntermediate(String name, long timeInNS) {
+        mIntermediates.add(new IntermediateTime(name, timeInNS));
+    }
+
+    public void finishTiming(boolean realTime) {
+        if (realTime) {
+            mEndTime = System.currentTimeMillis();
+        } else {
+            mEndTime = SystemClock.currentThreadTimeMillis();
+        }
+    }
+
+    public void setPerformanceMode(int mode) {
+        mMode = mode;
+    }
+
+    private void missingTest(String className, Throwable e) {
+        started(className);
+        finished(className);
+        failed(className, e);
+    }
+
+    /*
+    This class determines if more suites are added to this class then adds all individual
+    test classes to a test suite for run
+     */
+    public void run(String className) {
+        try {
+            mClassName = className;
+            Class clazz = mContext.getClassLoader().loadClass(className);
+            Method method = getChildrenMethod(clazz);
+            if (method != null) {
+                String[] children = getChildren(method);
+                run(children);
+            } else if (mRunnableClass.isAssignableFrom(clazz)) {
+                Runnable test = (Runnable) clazz.newInstance();
+                TestCase testcase = null;
+                if (test instanceof TestCase) {
+                    testcase = (TestCase) test;
+                }
+                Throwable e = null;
+                boolean didSetup = false;
+                started(className);
+                try {
+                    if (testcase != null) {
+                        testcase.setUp(mContext);
+                        didSetup = true;
+                    }
+                    if (mMode == PERFORMANCE) {
+                        runInPerformanceMode(test, className, false, className);
+                    } else if (mMode == PROFILING) {
+                        //Need a way to mark a test to be run in profiling mode or not. 
+                        startProfiling();
+                        test.run();
+                        finishProfiling();
+                    } else {
+                        test.run();
+                    }
+                } catch (Throwable ex) {
+                    e = ex;
+                }
+                if (testcase != null && didSetup) {
+                    try {
+                        testcase.tearDown();
+                    } catch (Throwable ex) {
+                        e = ex;
+                    }
+                }
+                finished(className);
+                if (e == null) {
+                    passed(className);
+                } else {
+                    failed(className, e);
+                }
+            } else if (mJUnitClass.isAssignableFrom(clazz)) {
+                Throwable e = null;
+                //Create a Junit Suite.
+                JunitTestSuite suite = new JunitTestSuite();
+                Method[] methods = getAllTestMethods(clazz);
+                for (Method m : methods) {
+                    junit.framework.TestCase test = (junit.framework.TestCase) clazz.newInstance();
+                    test.setName(m.getName());
+
+                    if (test instanceof AndroidTestCase) {
+                        AndroidTestCase testcase = (AndroidTestCase) test;
+                        try {
+                            testcase.setContext(mContext);
+                        } catch (Exception ex) {
+                            Log.i("TestHarness", ex.toString());
+                        }
+                    }
+                    suite.addTest(test);
+                }
+                if (mMode == PERFORMANCE) {
+                    final int testCount = suite.testCount();
+
+                    for (int j = 0; j < testCount; j++) {
+                        Test test = suite.testAt(j);
+                        started(test.toString());
+                        try {
+                            runInPerformanceMode(test, className, true, test.toString());
+                        } catch (Throwable ex) {
+                            e = ex;
+                        }
+                        finished(test.toString());
+                        if (e == null) {
+                            passed(test.toString());
+                        } else {
+                            failed(test.toString(), e);
+                        }
+                    }
+                } else if (mMode == PROFILING) {
+                    //Need a way to mark a test to be run in profiling mode or not.
+                    startProfiling();
+                    junit.textui.TestRunner.run(suite);
+                    finishProfiling();
+                } else {
+                    junit.textui.TestRunner.run(suite);
+                }
+            } else {
+                System.out.println("Test wasn't Runnable and didn't have a"
+                        + " children method: " + className);
+            }
+        } catch (ClassNotFoundException e) {
+            Log.e("ClassNotFoundException for " + className, e.toString());
+            if (isJunitTest(className)) {
+                runSingleJunitTest(className);
+            } else {
+                missingTest(className, e);
+            }
+        } catch (InstantiationException e) {
+            System.out.println("InstantiationException for " + className);
+            missingTest(className, e);
+        } catch (IllegalAccessException e) {
+            System.out.println("IllegalAccessException for " + className);
+            missingTest(className, e);
+        }
+    }
+
+    public void runInPerformanceMode(Object testCase, String className, boolean junitTest,
+            String testNameInDb) throws Exception {
+        boolean increaseIterations = true;
+        int iterations = 1;
+        long duration = 0;
+        mIntermediates = null;
+
+        mInternalIterations = 1;
+        Class clazz = mContext.getClassLoader().loadClass(className);
+        Object perftest = clazz.newInstance();
+
+        PerformanceTestCase perftestcase = null;
+        if (perftest instanceof PerformanceTestCase) {
+            perftestcase = (PerformanceTestCase) perftest;
+            // only run the test if it is not marked as a performance only test
+            if (mMode == REGRESSION && perftestcase.isPerformanceOnly()) return;
+        }
+
+        // First force GCs, to avoid GCs happening during out
+        // test and skewing its time.
+        Runtime.getRuntime().runFinalization();
+        Runtime.getRuntime().gc();
+
+        if (perftestcase != null) {
+            mIntermediates = new ArrayList<IntermediateTime>();
+            iterations = perftestcase.startPerformance(this);
+            if (iterations > 0) {
+                increaseIterations = false;
+            } else {
+                iterations = 1;
+            }
+        }
+
+        // Pause briefly to let things settle down...
+        Thread.sleep(1000);
+        do {
+            mEndTime = 0;
+            if (increaseIterations) {
+                // Test case does not implement
+                // PerformanceTestCase or returned 0 iterations,
+                // so we take care of measure the whole test time.
+                mStartTime = SystemClock.currentThreadTimeMillis();
+            } else {
+                // Try to make it obvious if the test case
+                // doesn't call startTiming().
+                mStartTime = 0;
+            }
+
+            if (junitTest) {
+                for (int i = 0; i < iterations; i++) {
+                    junit.textui.TestRunner.run((junit.framework.Test) testCase);
+                }
+            } else {
+                Runnable test = (Runnable) testCase;
+                for (int i = 0; i < iterations; i++) {
+                    test.run();
+                }
+            }
+
+            long endTime = mEndTime;
+            if (endTime == 0) {
+                endTime = SystemClock.currentThreadTimeMillis();
+            }
+
+            duration = endTime - mStartTime;
+            if (!increaseIterations) {
+                break;
+            }
+            if (duration <= 1) {
+                iterations *= 1000;
+            } else if (duration <= 10) {
+                iterations *= 100;
+            } else if (duration < 100) {
+                iterations *= 10;
+            } else if (duration < 1000) {
+                iterations *= (int) ((1000 / duration) + 2);
+            } else {
+                break;
+            }
+        } while (true);
+
+        if (duration != 0) {
+            iterations *= mInternalIterations;
+            performance(testNameInDb, (duration * 1000000) / iterations,
+                    iterations, mIntermediates);
+        }
+    }
+
+    public void runSingleJunitTest(String className) {
+        Throwable excep = null;
+        int index = className.lastIndexOf('$');
+        String testName = "";
+        String originalClassName = className;
+        if (index >= 0) {
+            className = className.substring(0, index);
+            testName = originalClassName.substring(index + 1);
+        }
+        try {
+            Class clazz = mContext.getClassLoader().loadClass(className);
+            if (mJUnitClass.isAssignableFrom(clazz)) {
+                junit.framework.TestCase test = (junit.framework.TestCase) clazz.newInstance();
+                JunitTestSuite newSuite = new JunitTestSuite();
+                test.setName(testName);
+
+                if (test instanceof AndroidTestCase) {
+                    AndroidTestCase testcase = (AndroidTestCase) test;
+                    try {
+                        testcase.setContext(mContext);
+                    } catch (Exception ex) {
+                        Log.w(TAG, "Exception encountered while trying to set the context.", ex);
+                    }
+                }
+                newSuite.addTest(test);
+
+                if (mMode == PERFORMANCE) {
+                    try {
+                        started(test.toString());
+                        runInPerformanceMode(test, className, true, test.toString());
+                        finished(test.toString());
+                        if (excep == null) {
+                            passed(test.toString());
+                        } else {
+                            failed(test.toString(), excep);
+                        }
+                    } catch (Throwable ex) {
+                        excep = ex;
+                    }
+
+                } else if (mMode == PROFILING) {
+                    startProfiling();
+                    junit.textui.TestRunner.run(newSuite);
+                    finishProfiling();
+                } else {
+                    junit.textui.TestRunner.run(newSuite);
+                }
+            }
+        } catch (ClassNotFoundException e) {
+            Log.e("TestHarness", "No test case to run", e);
+        } catch (IllegalAccessException e) {
+            Log.e("TestHarness", "Illegal Access Exception", e);
+        } catch (InstantiationException e) {
+            Log.e("TestHarness", "Instantiation Exception", e);
+        }
+    }
+
+    public static Method getChildrenMethod(Class clazz) {
+        try {
+            return clazz.getMethod("children", (Class[]) null);
+        } catch (NoSuchMethodException e) {
+        }
+
+        return null;
+    }
+
+    public static Method getChildrenMethod(Context c, String className) {
+        try {
+            return getChildrenMethod(c.getClassLoader().loadClass(className));
+        } catch (ClassNotFoundException e) {
+        }
+        return null;
+    }
+
+    public static String[] getChildren(Context c, String className) {
+        Method m = getChildrenMethod(c, className);
+        String[] testChildren = getTestChildren(c, className);
+        if (m == null & testChildren == null) {
+            throw new RuntimeException("couldn't get children method for "
+                    + className);
+        }
+        if (m != null) {
+            String[] children = getChildren(m);
+            if (testChildren != null) {
+                String[] allChildren = new String[testChildren.length + children.length];
+                System.arraycopy(children, 0, allChildren, 0, children.length);
+                System.arraycopy(testChildren, 0, allChildren, children.length, testChildren.length);
+                return allChildren;
+            } else {
+                return children;
+            }
+        } else {
+            if (testChildren != null) {
+                return testChildren;
+            }
+        }
+        return null;
+    }
+
+    public static String[] getChildren(Method m) {
+        try {
+            if (!Modifier.isStatic(m.getModifiers())) {
+                throw new RuntimeException("children method is not static");
+            }
+            return (String[]) m.invoke(null, (Object[]) null);
+        } catch (IllegalAccessException e) {
+        } catch (InvocationTargetException e) {
+        }
+        return new String[0];
+    }
+
+    public static String[] getTestChildren(Context c, String className) {
+        try {
+            Class clazz = c.getClassLoader().loadClass(className);
+
+            if (mJUnitClass.isAssignableFrom(clazz)) {
+                return getTestChildren(clazz);
+            }
+        } catch (ClassNotFoundException e) {
+            Log.e("TestHarness", "No class found", e);
+        }
+        return null;
+    }
+
+    public static String[] getTestChildren(Class clazz) {
+        Method[] methods = getAllTestMethods(clazz);
+
+        String[] onScreenTestNames = new String[methods.length];
+        int index = 0;
+        for (Method m : methods) {
+            onScreenTestNames[index] = clazz.getName() + "$" + m.getName();
+            index++;
+        }
+        return onScreenTestNames;
+    }
+
+    public static Method[] getAllTestMethods(Class clazz) {
+        Method[] allMethods = clazz.getDeclaredMethods();
+        int numOfMethods = 0;
+        for (Method m : allMethods) {
+            boolean mTrue = isTestMethod(m);
+            if (mTrue) {
+                numOfMethods++;
+            }
+        }
+        int index = 0;
+        Method[] testMethods = new Method[numOfMethods];
+        for (Method m : allMethods) {
+            boolean mTrue = isTestMethod(m);
+            if (mTrue) {
+                testMethods[index] = m;
+                index++;
+            }
+        }
+        return testMethods;
+    }
+
+    private static boolean isTestMethod(Method m) {
+        return m.getName().startsWith("test") &&
+                m.getReturnType() == void.class &&
+                m.getParameterTypes().length == 0;
+    }
+
+    public static int countJunitTests(Class clazz) {
+        Method[] allTestMethods = getAllTestMethods(clazz);
+        int numberofMethods = allTestMethods.length;
+
+        return numberofMethods;
+    }
+
+    public static boolean isTestSuite(Context c, String className) {
+        boolean childrenMethods = getChildrenMethod(c, className) != null;
+
+        try {
+            Class clazz = c.getClassLoader().loadClass(className);
+            if (mJUnitClass.isAssignableFrom(clazz)) {
+                int numTests = countJunitTests(clazz);
+                if (numTests > 0)
+                    childrenMethods = true;
+            }
+        } catch (ClassNotFoundException e) {
+        }
+        return childrenMethods;
+    }
+
+
+    public boolean isJunitTest(String className) {
+        int index = className.lastIndexOf('$');
+        if (index >= 0) {
+            className = className.substring(0, index);
+        }
+        try {
+            Class clazz = mContext.getClassLoader().loadClass(className);
+            if (mJUnitClass.isAssignableFrom(clazz)) {
+                return true;
+            }
+        } catch (ClassNotFoundException e) {
+        }
+        return false;
+    }
+
+    /**
+     * Returns the number of tests that will be run if you try to do this.
+     */
+    public static int countTests(Context c, String className) {
+        try {
+            Class clazz = c.getClassLoader().loadClass(className);
+            Method method = getChildrenMethod(clazz);
+            if (method != null) {
+
+                String[] children = getChildren(method);
+                int rv = 0;
+                for (String child : children) {
+                    rv += countTests(c, child);
+                }
+                return rv;
+            } else if (mRunnableClass.isAssignableFrom(clazz)) {
+                return 1;
+            } else if (mJUnitClass.isAssignableFrom(clazz)) {
+                return countJunitTests(clazz);
+            }
+        } catch (ClassNotFoundException e) {
+            return 1; // this gets the count right, because either this test
+            // is missing, and it will fail when run or it is a single Junit test to be run. 
+        }
+        return 0;
+    }
+
+    /**
+     * Returns a title to display given the className of a test.
+     * <p/>
+     * <p>Currently this function just returns the portion of the
+     * class name after the last '.'
+     */
+    public static String getTitle(String className) {
+        int indexDot = className.lastIndexOf('.');
+        int indexDollar = className.lastIndexOf('$');
+        int index = indexDot > indexDollar ? indexDot : indexDollar;
+        if (index >= 0) {
+            className = className.substring(index + 1);
+        }
+        return className;
+    }
+}
diff --git a/test-runner/android/test/TestRunnerView.java b/test-runner/android/test/TestRunnerView.java
new file mode 100644
index 0000000..be90951
--- /dev/null
+++ b/test-runner/android/test/TestRunnerView.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2007 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.test;
+
+import java.util.List;
+
+/**
+ * @hide - This is part of a framework that is under development and should not be used for
+ * active development.
+ */
+public interface TestRunnerView {
+    void setTestNames(List<String> testNames);
+
+    void setItemColorAt(int position, int color);
+
+    void setFailureCount(int failureCount);
+
+    void setRunCount(int runCount);
+
+    void setErrorCount(int errorCount);
+
+    void setTotalCount(int totalCount);
+
+    void setProgressBarColor(int color);
+
+    void testsCompleted();
+}
diff --git a/test-runner/android/test/TestSuiteProvider.java b/test-runner/android/test/TestSuiteProvider.java
new file mode 100644
index 0000000..dc9ce6e
--- /dev/null
+++ b/test-runner/android/test/TestSuiteProvider.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+package android.test;
+
+import junit.framework.TestSuite;
+
+/**
+ * Implementors will know how to get a test suite.
+ */
+public interface TestSuiteProvider {
+
+    TestSuite getTestSuite();
+}
diff --git a/test-runner/android/test/TouchUtils.java b/test-runner/android/test/TouchUtils.java
new file mode 100644
index 0000000..52d2ee8
--- /dev/null
+++ b/test-runner/android/test/TouchUtils.java
@@ -0,0 +1,793 @@
+/*
+ * Copyright (C) 2007 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.test;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.os.SystemClock;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+
+/**
+ * Reusable methods for generating touch events. These methods can be used with
+ * InstrumentationTestCase or ActivityInstrumentationTestCase2 to simulate user interaction with
+ * the application through a touch screen.
+ */
+public class TouchUtils {
+    
+    /**
+     * Simulate touching in the center of the screen and dragging one quarter of the way down
+     * @param test The test case that is being run
+     *
+     * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
+     * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+     * configuring the Activity under test
+     */
+    @Deprecated
+    public static void dragQuarterScreenDown(ActivityInstrumentationTestCase test) {
+        dragQuarterScreenDown(test, test.getActivity());
+    }
+    
+    /**
+     * Simulate touching in the center of the screen and dragging one quarter of the way down
+     * @param test The test case that is being run
+     * @param activity The activity that is in the foreground of the test case
+     */
+    public static void dragQuarterScreenDown(InstrumentationTestCase test, Activity activity) {
+        Display display = activity.getWindowManager().getDefaultDisplay();
+        int screenHeight = display.getHeight();
+        int screenWidth = display.getWidth();
+        
+        final float x = screenWidth / 2.0f;
+        final float fromY = screenHeight * 0.5f;
+        final float toY = screenHeight * 0.75f;
+      
+        drag(test, x, x, fromY, toY, 4);
+    }
+    
+    /**
+     * Simulate touching in the center of the screen and dragging one quarter of the way up
+     * @param test The test case that is being run
+     *
+     * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
+     * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+     * configuring the Activity under test
+     */
+    @Deprecated
+    public static void dragQuarterScreenUp(ActivityInstrumentationTestCase test) {
+        dragQuarterScreenUp(test, test.getActivity());
+    }
+    
+    /**
+     * Simulate touching in the center of the screen and dragging one quarter of the way up
+     * @param test The test case that is being run
+     * @param activity The activity that is in the foreground of the test case
+     */
+    public static void dragQuarterScreenUp(InstrumentationTestCase test, Activity activity) {
+        Display display = activity.getWindowManager().getDefaultDisplay();
+        int screenHeight = display.getHeight();
+        int screenWidth = display.getWidth();
+        
+        final float x = screenWidth / 2.0f;
+        final float fromY = screenHeight * 0.5f;
+        final float toY = screenHeight * 0.25f;
+      
+        drag(test, x, x, fromY, toY, 4);
+    }
+    
+    /**
+     * Scroll a ViewGroup to the bottom by repeatedly calling
+     * {@link #dragQuarterScreenUp(InstrumentationTestCase, Activity)}
+     * 
+     * @param test The test case that is being run
+     * @param v The ViewGroup that should be dragged
+     *
+     * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
+     * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+     * configuring the Activity under test
+     */
+    @Deprecated
+    public static void scrollToBottom(ActivityInstrumentationTestCase test, ViewGroup v) {
+        scrollToBottom(test, test.getActivity(), v);
+    }
+    
+    /**
+     * Scroll a ViewGroup to the bottom by repeatedly calling
+     * {@link #dragQuarterScreenUp(InstrumentationTestCase, Activity)}
+     * 
+     * @param test The test case that is being run
+     * @param activity The activity that is in the foreground of the test case
+     * @param v The ViewGroup that should be dragged
+     */
+    public static void scrollToBottom(InstrumentationTestCase test, Activity activity,
+            ViewGroup v) {
+        View firstChild;
+        int firstId = Integer.MIN_VALUE;
+        int firstTop = Integer.MIN_VALUE; 
+        int prevId;
+        int prevTop; 
+        do {
+            prevId = firstId;
+            prevTop = firstTop;
+            TouchUtils.dragQuarterScreenUp(test, activity);
+            firstChild = v.getChildAt(0);
+            firstId = firstChild.getId();
+            firstTop = firstChild.getTop(); 
+        } while ((prevId != firstId) || (prevTop != firstTop));
+    }
+
+    /**
+     * Scroll a ViewGroup to the top by repeatedly calling
+     * {@link #dragQuarterScreenDown(InstrumentationTestCase, Activity)}
+     * 
+     * @param test The test case that is being run
+     * @param v The ViewGroup that should be dragged
+     *
+     * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
+     * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+     * configuring the Activity under test
+     */
+    @Deprecated
+    public static void scrollToTop(ActivityInstrumentationTestCase test, ViewGroup v) {
+        scrollToTop(test, test.getActivity(), v);
+    }
+    
+    /**
+     * Scroll a ViewGroup to the top by repeatedly calling
+     * {@link #dragQuarterScreenDown(InstrumentationTestCase, Activity)}
+     * 
+     * @param test The test case that is being run
+     * @param activity The activity that is in the foreground of the test case
+     * @param v The ViewGroup that should be dragged
+     */
+    public static void scrollToTop(InstrumentationTestCase test, Activity activity, ViewGroup v) {
+        View firstChild;
+        int firstId = Integer.MIN_VALUE;
+        int firstTop = Integer.MIN_VALUE; 
+        int prevId;
+        int prevTop; 
+        do {
+            prevId = firstId;
+            prevTop = firstTop;
+            TouchUtils.dragQuarterScreenDown(test, activity);
+            firstChild = v.getChildAt(0);
+            firstId = firstChild.getId();
+            firstTop = firstChild.getTop(); 
+        } while ((prevId != firstId) || (prevTop != firstTop));
+    }
+    
+    /**
+     * Simulate touching the center of a view and dragging to the bottom of the screen.
+     * 
+     * @param test The test case that is being run
+     * @param v The view that should be dragged
+     *
+     * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
+     * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+     * configuring the Activity under test
+     */
+    @Deprecated
+    public static void dragViewToBottom(ActivityInstrumentationTestCase test, View v) {
+        dragViewToBottom(test, test.getActivity(), v, 4);
+    }
+    
+    /**
+     * Simulate touching the center of a view and dragging to the bottom of the screen.
+     * 
+     * @param test The test case that is being run
+     * @param activity The activity that is in the foreground of the test case
+     * @param v The view that should be dragged
+     */
+    public static void dragViewToBottom(InstrumentationTestCase test, Activity activity, View v) {
+        dragViewToBottom(test, activity, v, 4);
+    }
+    
+    /**
+     * Simulate touching the center of a view and dragging to the bottom of the screen.
+     * 
+     * @param test The test case that is being run
+     * @param v The view that should be dragged
+     * @param stepCount How many move steps to include in the drag
+     *
+     * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
+     * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+     * configuring the Activity under test
+     */
+    @Deprecated
+    public static void dragViewToBottom(ActivityInstrumentationTestCase test, View v,
+            int stepCount) {
+        dragViewToBottom(test, test.getActivity(), v, stepCount);
+    }
+    
+    /**
+     * Simulate touching the center of a view and dragging to the bottom of the screen.
+     * 
+     * @param test The test case that is being run
+     * @param activity The activity that is in the foreground of the test case
+     * @param v The view that should be dragged
+     * @param stepCount How many move steps to include in the drag
+     */
+    public static void dragViewToBottom(InstrumentationTestCase test, Activity activity, View v,
+            int stepCount) {
+        int screenHeight = activity.getWindowManager().getDefaultDisplay().getHeight();
+
+        int[] xy = new int[2];
+        v.getLocationOnScreen(xy);
+        
+        final int viewWidth = v.getWidth();
+        final int viewHeight = v.getHeight();
+        
+        final float x = xy[0] + (viewWidth / 2.0f);
+        float fromY = xy[1] + (viewHeight / 2.0f);
+        float toY = screenHeight - 1;
+        
+        drag(test, x, x, fromY, toY, stepCount);
+    }
+
+    /**
+     * Simulate touching the center of a view and releasing quickly (before the tap timeout).
+     * 
+     * @param test The test case that is being run
+     * @param v The view that should be clicked
+     */
+    public static void tapView(InstrumentationTestCase test, View v) {
+        int[] xy = new int[2];
+        v.getLocationOnScreen(xy);
+        
+        final int viewWidth = v.getWidth();
+        final int viewHeight = v.getHeight();
+        
+        final float x = xy[0] + (viewWidth / 2.0f);
+        float y = xy[1] + (viewHeight / 2.0f);
+        
+        Instrumentation inst = test.getInstrumentation();
+
+        long downTime = SystemClock.uptimeMillis();
+        long eventTime = SystemClock.uptimeMillis();
+        
+        MotionEvent event = MotionEvent.obtain(downTime, eventTime,
+                MotionEvent.ACTION_DOWN, x, y, 0);
+        inst.sendPointerSync(event);
+        inst.waitForIdleSync();
+
+        eventTime = SystemClock.uptimeMillis();
+        final int touchSlop = ViewConfiguration.get(v.getContext()).getScaledTouchSlop();
+        event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE,
+                x + (touchSlop / 2.0f), y + (touchSlop / 2.0f), 0);
+        inst.sendPointerSync(event);
+        inst.waitForIdleSync();
+
+        eventTime = SystemClock.uptimeMillis();
+        event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0);
+        inst.sendPointerSync(event);
+        inst.waitForIdleSync();
+    }
+
+    /**
+     * Simulate touching the center of a view and cancelling (so no onClick should
+     * fire, etc).
+     *
+     * @param test The test case that is being run
+     * @param v The view that should be clicked
+     */
+    public static void touchAndCancelView(InstrumentationTestCase test, View v) {
+        int[] xy = new int[2];
+        v.getLocationOnScreen(xy);
+
+        final int viewWidth = v.getWidth();
+        final int viewHeight = v.getHeight();
+
+        final float x = xy[0] + (viewWidth / 2.0f);
+        float y = xy[1] + (viewHeight / 2.0f);
+
+        Instrumentation inst = test.getInstrumentation();
+
+        long downTime = SystemClock.uptimeMillis();
+        long eventTime = SystemClock.uptimeMillis();
+
+        MotionEvent event = MotionEvent.obtain(downTime, eventTime,
+                MotionEvent.ACTION_DOWN, x, y, 0);
+        inst.sendPointerSync(event);
+        inst.waitForIdleSync();
+
+        eventTime = SystemClock.uptimeMillis();
+        final int touchSlop = ViewConfiguration.get(v.getContext()).getScaledTouchSlop();
+        event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_CANCEL,
+                x + (touchSlop / 2.0f), y + (touchSlop / 2.0f), 0);
+        inst.sendPointerSync(event);
+        inst.waitForIdleSync();
+
+    }
+
+    /**
+     * Simulate touching the center of a view and releasing.
+     * 
+     * @param test The test case that is being run
+     * @param v The view that should be clicked
+     */
+    public static void clickView(InstrumentationTestCase test, View v) {
+        int[] xy = new int[2];
+        v.getLocationOnScreen(xy);
+        
+        final int viewWidth = v.getWidth();
+        final int viewHeight = v.getHeight();
+        
+        final float x = xy[0] + (viewWidth / 2.0f);
+        float y = xy[1] + (viewHeight / 2.0f);
+        
+        Instrumentation inst = test.getInstrumentation();
+
+        long downTime = SystemClock.uptimeMillis();
+        long eventTime = SystemClock.uptimeMillis();
+        
+        MotionEvent event = MotionEvent.obtain(downTime, eventTime,
+                MotionEvent.ACTION_DOWN, x, y, 0);
+        inst.sendPointerSync(event);
+        inst.waitForIdleSync();
+        
+
+        eventTime = SystemClock.uptimeMillis();
+        final int touchSlop = ViewConfiguration.get(v.getContext()).getScaledTouchSlop();
+        event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE,
+                x + (touchSlop / 2.0f), y + (touchSlop / 2.0f), 0);
+        inst.sendPointerSync(event);
+        inst.waitForIdleSync();
+
+        eventTime = SystemClock.uptimeMillis();
+        event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0);
+        inst.sendPointerSync(event);
+        inst.waitForIdleSync();
+        
+        try {
+            Thread.sleep(1000);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Simulate touching the center of a view, holding until it is a long press, and then releasing.
+     * 
+     * @param test The test case that is being run
+     * @param v The view that should be clicked
+     *
+     * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
+     * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+     * configuring the Activity under test
+     */
+    @Deprecated
+    public static void longClickView(ActivityInstrumentationTestCase test, View v) {
+        longClickView((InstrumentationTestCase) test, v);
+    }
+
+    /**
+     * Simulate touching the center of a view, holding until it is a long press, and then releasing.
+     * 
+     * @param test The test case that is being run
+     * @param v The view that should be clicked
+     */
+    public static void longClickView(InstrumentationTestCase test, View v) {
+        int[] xy = new int[2];
+        v.getLocationOnScreen(xy);
+        
+        final int viewWidth = v.getWidth();
+        final int viewHeight = v.getHeight();
+        
+        final float x = xy[0] + (viewWidth / 2.0f);
+        float y = xy[1] + (viewHeight / 2.0f);
+        
+        Instrumentation inst = test.getInstrumentation();
+
+        long downTime = SystemClock.uptimeMillis();
+        long eventTime = SystemClock.uptimeMillis();
+
+        MotionEvent event = MotionEvent.obtain(downTime, eventTime,
+                MotionEvent.ACTION_DOWN, x, y, 0);
+        inst.sendPointerSync(event);
+        inst.waitForIdleSync();
+
+        eventTime = SystemClock.uptimeMillis();
+        final int touchSlop = ViewConfiguration.get(v.getContext()).getScaledTouchSlop();
+        event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE,
+                x + touchSlop / 2, y + touchSlop / 2, 0);
+        inst.sendPointerSync(event);
+        inst.waitForIdleSync();
+        
+        try {
+            Thread.sleep((long)(ViewConfiguration.getLongPressTimeout() * 1.5f));
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+
+        eventTime = SystemClock.uptimeMillis();
+        event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0);
+        inst.sendPointerSync(event);
+        inst.waitForIdleSync();
+    }
+
+    /**
+     * Simulate touching the center of a view and dragging to the top of the screen.
+     * 
+     * @param test The test case that is being run
+     * @param v The view that should be dragged
+     *
+     * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
+     * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+     * configuring the Activity under test
+     */
+    @Deprecated
+    public static void dragViewToTop(ActivityInstrumentationTestCase test, View v) {
+        dragViewToTop((InstrumentationTestCase) test, v, 4);
+    }
+    
+    /**
+     * Simulate touching the center of a view and dragging to the top of the screen.
+     * 
+     * @param test The test case that is being run
+     * @param v The view that should be dragged
+     * @param stepCount How many move steps to include in the drag
+     *
+     * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
+     * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+     * configuring the Activity under test
+     */
+    @Deprecated
+    public static void dragViewToTop(ActivityInstrumentationTestCase test, View v, int stepCount) {
+        dragViewToTop((InstrumentationTestCase) test, v, stepCount);
+    }
+    
+    /**
+     * Simulate touching the center of a view and dragging to the top of the screen.
+     * 
+     * @param test The test case that is being run
+     * @param v The view that should be dragged
+     */
+    public static void dragViewToTop(InstrumentationTestCase test, View v) {
+        dragViewToTop(test, v, 4);
+    }
+    
+    /**
+     * Simulate touching the center of a view and dragging to the top of the screen.
+     * 
+     * @param test The test case that is being run
+     * @param v The view that should be dragged
+     * @param stepCount How many move steps to include in the drag
+     */
+    public static void dragViewToTop(InstrumentationTestCase test, View v, int stepCount) {
+        int[] xy = new int[2];
+        v.getLocationOnScreen(xy);
+        
+        final int viewWidth = v.getWidth();
+        final int viewHeight = v.getHeight();
+        
+        final float x = xy[0] + (viewWidth / 2.0f);
+        float fromY = xy[1] + (viewHeight / 2.0f);
+        float toY = 0;
+        
+        drag(test, x, x, fromY, toY, stepCount);
+    }
+    
+    /**
+     * Get the location of a view. Use the gravity param to specify which part of the view to
+     * return.
+     * 
+     * @param v View to find
+     * @param gravity A combination of (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL,
+     *        RIGHT)
+     * @param xy Result
+     */
+    private static void getStartLocation(View v, int gravity, int[] xy) {
+        v.getLocationOnScreen(xy);
+
+        final int viewWidth = v.getWidth();
+        final int viewHeight = v.getHeight();
+        
+        switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) {
+        case Gravity.TOP:
+            break;
+        case Gravity.CENTER_VERTICAL:
+            xy[1] += viewHeight / 2;
+            break;
+        case Gravity.BOTTOM:
+            xy[1] += viewHeight - 1;
+            break;
+        default:
+            // Same as top -- do nothing
+        }
+        
+        switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
+        case Gravity.LEFT:
+            break;
+        case Gravity.CENTER_HORIZONTAL:
+            xy[0] += viewWidth / 2;
+            break;
+        case Gravity.RIGHT:
+            xy[0] += viewWidth - 1;
+            break;
+        default:
+            // Same as left -- do nothing
+        }
+    }
+
+    /**
+     * Simulate touching a view and dragging it by the specified amount.
+     * 
+     * @param test The test case that is being run
+     * @param v The view that should be dragged
+     * @param gravity Which part of the view to use for the initial down event. A combination of
+     *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
+     * @param deltaX Amount to drag horizontally in pixels
+     * @param deltaY Amount to drag vertically in pixels
+     * 
+     * @return distance in pixels covered by the drag
+     *
+     * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
+     * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+     * configuring the Activity under test
+     */
+    @Deprecated
+    public static int dragViewBy(ActivityInstrumentationTestCase test, View v, int gravity,
+            int deltaX, int deltaY) {
+        return dragViewBy((InstrumentationTestCase) test, v, gravity, deltaX, deltaY);
+    }
+    
+    /**
+     * Simulate touching a view and dragging it by the specified amount.
+     * 
+     * @param test The test case that is being run
+     * @param v The view that should be dragged
+     * @param gravity Which part of the view to use for the initial down event. A combination of
+     *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
+     * @param deltaX Amount to drag horizontally in pixels
+     * @param deltaY Amount to drag vertically in pixels
+     * 
+     * @return distance in pixels covered by the drag
+     *
+     * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
+     * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+     * configuring the Activity under test
+     */
+    public static int dragViewBy(InstrumentationTestCase test, View v, int gravity, int deltaX,
+            int deltaY) {
+        int[] xy = new int[2];
+        
+        getStartLocation(v, gravity, xy);
+
+        final int fromX = xy[0];
+        final int fromY = xy[1];
+        
+        int distance = (int) Math.sqrt(deltaX * deltaX + deltaY * deltaY);
+
+        drag(test, fromX, fromX + deltaX, fromY, fromY + deltaY, distance);
+
+        return distance;
+    }
+    
+    /**
+     * Simulate touching a view and dragging it to a specified location.
+     * 
+     * @param test The test case that is being run
+     * @param v The view that should be dragged
+     * @param gravity Which part of the view to use for the initial down event. A combination of
+     *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
+     * @param toX Final location of the view after dragging
+     * @param toY Final location of the view after dragging
+     * 
+     * @return distance in pixels covered by the drag
+     *
+     * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
+     * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+     * configuring the Activity under test
+     */
+    @Deprecated
+    public static int dragViewTo(ActivityInstrumentationTestCase test, View v, int gravity, int toX,
+            int toY) {
+        return dragViewTo((InstrumentationTestCase) test, v, gravity, toX, toY);
+    }
+
+    /**
+     * Simulate touching a view and dragging it to a specified location.
+     * 
+     * @param test The test case that is being run
+     * @param v The view that should be dragged
+     * @param gravity Which part of the view to use for the initial down event. A combination of
+     *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
+     * @param toX Final location of the view after dragging
+     * @param toY Final location of the view after dragging
+     * 
+     * @return distance in pixels covered by the drag
+     */
+    public static int dragViewTo(InstrumentationTestCase test, View v, int gravity, int toX,
+            int toY) {
+        int[] xy = new int[2];
+
+        getStartLocation(v, gravity, xy);
+        
+        final int fromX = xy[0];
+        final int fromY = xy[1];
+        
+        int deltaX = fromX - toX;
+        int deltaY = fromY - toY;
+        
+        int distance = (int)Math.sqrt(deltaX * deltaX + deltaY * deltaY);
+        drag(test, fromX, toX, fromY, toY, distance);
+        
+        return distance;
+    }
+
+    /**
+     * Simulate touching a view and dragging it to a specified location. Only moves horizontally.
+     * 
+     * @param test The test case that is being run
+     * @param v The view that should be dragged
+     * @param gravity Which part of the view to use for the initial down event. A combination of
+     *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
+     * @param toX Final location of the view after dragging
+     * 
+     * @return distance in pixels covered by the drag
+     *
+     * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
+     * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+     * configuring the Activity under test
+     */
+    @Deprecated
+    public static int dragViewToX(ActivityInstrumentationTestCase test, View v, int gravity,
+            int toX) {
+        return dragViewToX((InstrumentationTestCase) test, v, gravity, toX);
+    }
+
+    /**
+     * Simulate touching a view and dragging it to a specified location. Only moves horizontally.
+     * 
+     * @param test The test case that is being run
+     * @param v The view that should be dragged
+     * @param gravity Which part of the view to use for the initial down event. A combination of
+     *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
+     * @param toX Final location of the view after dragging
+     * 
+     * @return distance in pixels covered by the drag
+     */
+    public static int dragViewToX(InstrumentationTestCase test, View v, int gravity, int toX) {
+        int[] xy = new int[2];
+
+        getStartLocation(v, gravity, xy);
+        
+        final int fromX = xy[0];
+        final int fromY = xy[1];
+        
+        int deltaX = fromX - toX;
+
+        drag(test, fromX, toX, fromY, fromY, deltaX);
+        
+        return deltaX;
+    }
+    
+    /**
+     * Simulate touching a view and dragging it to a specified location. Only moves vertically.
+     * 
+     * @param test The test case that is being run
+     * @param v The view that should be dragged
+     * @param gravity Which part of the view to use for the initial down event. A combination of
+     *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
+     * @param toY Final location of the view after dragging
+     * 
+     * @return distance in pixels covered by the drag
+     *
+     * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
+     * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+     * configuring the Activity under test
+     */
+    @Deprecated
+    public static int dragViewToY(ActivityInstrumentationTestCase test, View v, int gravity,
+            int toY) {
+        return dragViewToY((InstrumentationTestCase) test, v, gravity, toY);
+    }
+    
+    /**
+     * Simulate touching a view and dragging it to a specified location. Only moves vertically.
+     * 
+     * @param test The test case that is being run
+     * @param v The view that should be dragged
+     * @param gravity Which part of the view to use for the initial down event. A combination of
+     *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
+     * @param toY Final location of the view after dragging
+     * 
+     * @return distance in pixels covered by the drag
+     */
+    public static int dragViewToY(InstrumentationTestCase test, View v, int gravity, int toY) {
+        int[] xy = new int[2];
+
+        getStartLocation(v, gravity, xy);
+       
+        final int fromX = xy[0];
+        final int fromY = xy[1];
+        
+        int deltaY = fromY - toY;
+
+        drag(test, fromX, fromX, fromY, toY, deltaY);
+        
+        return deltaY;
+    }
+    
+
+    /**
+     * Simulate touching a specific location and dragging to a new location.
+     * 
+     * @param test The test case that is being run
+     * @param fromX X coordinate of the initial touch, in screen coordinates
+     * @param toX Xcoordinate of the drag destination, in screen coordinates
+     * @param fromY X coordinate of the initial touch, in screen coordinates
+     * @param toY Y coordinate of the drag destination, in screen coordinates
+     * @param stepCount How many move steps to include in the drag
+     *
+     * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
+     * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
+     * configuring the Activity under test
+     */
+    @Deprecated
+    public static void drag(ActivityInstrumentationTestCase test, float fromX, float toX,
+            float fromY, float toY, int stepCount) {
+        drag((InstrumentationTestCase) test, fromX, toX, fromY, toY, stepCount);
+    }
+    
+    /**
+     * Simulate touching a specific location and dragging to a new location.
+     * 
+     * @param test The test case that is being run
+     * @param fromX X coordinate of the initial touch, in screen coordinates
+     * @param toX Xcoordinate of the drag destination, in screen coordinates
+     * @param fromY X coordinate of the initial touch, in screen coordinates
+     * @param toY Y coordinate of the drag destination, in screen coordinates
+     * @param stepCount How many move steps to include in the drag
+     */
+    public static void drag(InstrumentationTestCase test, float fromX, float toX, float fromY,
+            float toY, int stepCount) {
+        Instrumentation inst = test.getInstrumentation();
+
+        long downTime = SystemClock.uptimeMillis();
+        long eventTime = SystemClock.uptimeMillis();
+
+        float y = fromY;
+        float x = fromX;
+        
+        float yStep = (toY - fromY) / stepCount;
+        float xStep = (toX - fromX) / stepCount;
+
+        MotionEvent event = MotionEvent.obtain(downTime, eventTime,
+                MotionEvent.ACTION_DOWN, fromX, y, 0);
+        inst.sendPointerSync(event);
+        inst.waitForIdleSync();
+
+        for (int i = 0; i < stepCount; ++i) {
+            y += yStep;
+            x += xStep;
+            eventTime = SystemClock.uptimeMillis();
+            event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, x, y, 0);
+            inst.sendPointerSync(event);
+            inst.waitForIdleSync();
+        }
+
+        eventTime = SystemClock.uptimeMillis();
+        event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, fromX, y, 0);
+        inst.sendPointerSync(event);
+        inst.waitForIdleSync();
+    }
+}
diff --git a/test-runner/android/test/ViewAsserts.java b/test-runner/android/test/ViewAsserts.java
new file mode 100644
index 0000000..c575fc5
--- /dev/null
+++ b/test-runner/android/test/ViewAsserts.java
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2007 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.test;
+
+import static junit.framework.Assert.*;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Some useful assertions about views.
+ */
+public class ViewAsserts {
+
+    private ViewAsserts() {}
+
+    /**
+     * Assert that view is on the screen.
+     * @param origin The root view of the screen.
+     * @param view The view.
+     */
+    static public void assertOnScreen(View origin, View view) {
+        int[] xy = new int[2];
+        view.getLocationOnScreen(xy);
+
+        int[] xyRoot = new int[2];
+        origin.getLocationOnScreen(xyRoot);
+
+        int y = xy[1] - xyRoot[1];
+
+        assertTrue("view should have positive y coordinate on screen",
+                y  >= 0);
+
+        assertTrue("view should have y location on screen less than drawing "
+                + "height of root view",
+                y <= view.getRootView().getHeight());
+    }
+
+    /**
+     * Assert that view is below the visible screen.
+     * @param origin The root view of the screen.
+     * @param view The view
+     */
+    static public void assertOffScreenBelow(View origin, View view) {
+        int[] xy = new int[2];
+        view.getLocationOnScreen(xy);
+
+        int[] xyRoot = new int[2];
+        origin.getLocationOnScreen(xyRoot);
+
+        int y = xy[1] - xyRoot[1];
+
+        assertTrue("view should have y location on screen greater than drawing "
+                + "height of origen view (" + y + " is not greater than "
+                + origin.getHeight() + ")",
+                y > origin.getHeight());
+    }
+
+    /**
+     * Assert that view is above the visible screen.
+     * @param origin Te root view of the screen.
+     * @param view The view
+     */
+    static public void assertOffScreenAbove(View origin, View view) {
+        int[] xy = new int[2];
+        view.getLocationOnScreen(xy);
+
+        int[] xyRoot = new int[2];
+        origin.getLocationOnScreen(xyRoot);
+
+        int y = xy[1] - xyRoot[1];
+
+        assertTrue("view should have y location less than that of origin view",
+                y < 0);
+    }
+
+    /**
+     * Assert that a view has a particular x and y position on the visible screen.
+     * @param origin The root view of the screen.
+     * @param view The view.
+     * @param x The expected x coordinate.
+     * @param y The expected y coordinate.
+     */
+    static public void assertHasScreenCoordinates(View origin, View view, int x, int y) {
+        int[] xy = new int[2];
+        view.getLocationOnScreen(xy);
+
+        int[] xyRoot = new int[2];
+        origin.getLocationOnScreen(xyRoot);
+
+        assertEquals("x coordinate", x, xy[0] - xyRoot[0]);
+        assertEquals("y coordinate", y, xy[1] - xyRoot[1]);
+    }
+
+    /**
+     * Assert that two views are aligned on their baseline, that is that their baselines
+     * are on the same y location.
+     *
+     * @param first The first view
+     * @param second The second view
+     */
+    static public void assertBaselineAligned(View first, View second) {
+        int[] xy = new int[2];
+        first.getLocationOnScreen(xy);
+        int firstTop = xy[1] + first.getBaseline();
+
+        second.getLocationOnScreen(xy);
+        int secondTop = xy[1] + second.getBaseline();
+
+        assertEquals("views are not baseline aligned", firstTop, secondTop);
+    }
+
+    /**
+     * Assert that two views are right aligned, that is that their right edges
+     * are on the same x location.
+     *
+     * @param first The first view
+     * @param second The second view
+     */
+    static public void assertRightAligned(View first, View second) {
+        int[] xy = new int[2];
+        first.getLocationOnScreen(xy);
+        int firstRight = xy[0] + first.getMeasuredWidth();
+
+        second.getLocationOnScreen(xy);
+        int secondRight = xy[0] + second.getMeasuredWidth();
+
+        assertEquals("views are not right aligned", firstRight, secondRight);
+    }
+
+    /**
+     * Assert that two views are right aligned, that is that their right edges
+     * are on the same x location, with respect to the specified margin.
+     *
+     * @param first The first view
+     * @param second The second view
+     * @param margin The margin between the first view and the second view
+     */
+    static public void assertRightAligned(View first, View second, int margin) {
+        int[] xy = new int[2];
+        first.getLocationOnScreen(xy);
+        int firstRight = xy[0] + first.getMeasuredWidth();
+
+        second.getLocationOnScreen(xy);
+        int secondRight = xy[0] + second.getMeasuredWidth();
+
+        assertEquals("views are not right aligned", Math.abs(firstRight - secondRight), margin);
+    }
+
+    /**
+     * Assert that two views are left aligned, that is that their left edges
+     * are on the same x location.
+     *
+     * @param first The first view
+     * @param second The second view
+     */
+    static public void assertLeftAligned(View first, View second) {
+        int[] xy = new int[2];
+        first.getLocationOnScreen(xy);
+        int firstLeft = xy[0];
+
+        second.getLocationOnScreen(xy);
+        int secondLeft = xy[0];
+
+        assertEquals("views are not left aligned", firstLeft, secondLeft);
+    }
+
+    /**
+     * Assert that two views are left aligned, that is that their left edges
+     * are on the same x location, with respect to the specified margin.
+     *
+     * @param first The first view
+     * @param second The second view
+     * @param margin The margin between the first view and the second view
+     */
+    static public void assertLeftAligned(View first, View second, int margin) {
+        int[] xy = new int[2];
+        first.getLocationOnScreen(xy);
+        int firstLeft = xy[0];
+
+        second.getLocationOnScreen(xy);
+        int secondLeft = xy[0];
+
+        assertEquals("views are not left aligned", Math.abs(firstLeft - secondLeft), margin);
+    }
+
+    /**
+     * Assert that two views are bottom aligned, that is that their bottom edges
+     * are on the same y location.
+     *
+     * @param first The first view
+     * @param second The second view
+     */
+    static public void assertBottomAligned(View first, View second) {
+        int[] xy = new int[2];
+        first.getLocationOnScreen(xy);
+        int firstBottom = xy[1] + first.getMeasuredHeight();
+
+        second.getLocationOnScreen(xy);
+        int secondBottom = xy[1] + second.getMeasuredHeight();
+
+        assertEquals("views are not bottom aligned", firstBottom, secondBottom);
+    }
+
+    /**
+     * Assert that two views are bottom aligned, that is that their bottom edges
+     * are on the same y location, with respect to the specified margin.
+     *
+     * @param first The first view
+     * @param second The second view
+     * @param margin The margin between the first view and the second view
+     */
+    static public void assertBottomAligned(View first, View second, int margin) {
+        int[] xy = new int[2];
+        first.getLocationOnScreen(xy);
+        int firstBottom = xy[1] + first.getMeasuredHeight();
+
+        second.getLocationOnScreen(xy);
+        int secondBottom = xy[1] + second.getMeasuredHeight();
+
+        assertEquals("views are not bottom aligned", Math.abs(firstBottom - secondBottom), margin);
+    }
+
+    /**
+     * Assert that two views are top aligned, that is that their top edges
+     * are on the same y location.
+     *
+     * @param first The first view
+     * @param second The second view
+     */
+    static public void assertTopAligned(View first, View second) {
+        int[] xy = new int[2];
+        first.getLocationOnScreen(xy);
+        int firstTop = xy[1];
+
+        second.getLocationOnScreen(xy);
+        int secondTop = xy[1];
+
+        assertEquals("views are not top aligned", firstTop, secondTop);
+    }
+
+    /**
+     * Assert that two views are top aligned, that is that their top edges
+     * are on the same y location, with respect to the specified margin.
+     *
+     * @param first The first view
+     * @param second The second view
+     * @param margin The margin between the first view and the second view
+     */
+    static public void assertTopAligned(View first, View second, int margin) {
+        int[] xy = new int[2];
+        first.getLocationOnScreen(xy);
+        int firstTop = xy[1];
+
+        second.getLocationOnScreen(xy);
+        int secondTop = xy[1];
+
+        assertEquals("views are not top aligned", Math.abs(firstTop - secondTop), margin);
+    }
+
+    /**
+     * Assert that the <code>test</code> view is horizontally center aligned
+     * with respect to the <code>reference</code> view.
+     *
+     * @param reference The reference view
+     * @param test The view that should be center aligned with the reference view
+     */
+    static public void assertHorizontalCenterAligned(View reference, View test) {
+        int[] xy = new int[2];
+        reference.getLocationOnScreen(xy);
+        int referenceLeft = xy[0];
+
+        test.getLocationOnScreen(xy);
+        int testLeft = xy[0];
+
+        int center = (reference.getMeasuredWidth() - test.getMeasuredWidth()) / 2;
+        int delta = testLeft - referenceLeft;
+
+        assertEquals("views are not horizontally center aligned", center, delta);
+    }
+
+    /**
+     * Assert that the <code>test</code> view is vertically center aligned
+     * with respect to the <code>reference</code> view.
+     *
+     * @param reference The reference view
+     * @param test The view that should be center aligned with the reference view
+     */
+    static public void assertVerticalCenterAligned(View reference, View test) {
+        int[] xy = new int[2];
+        reference.getLocationOnScreen(xy);
+        int referenceTop = xy[1];
+
+        test.getLocationOnScreen(xy);
+        int testTop = xy[1];
+
+        int center = (reference.getMeasuredHeight() - test.getMeasuredHeight()) / 2;
+        int delta = testTop - referenceTop;
+
+        assertEquals("views are not vertically center aligned", center, delta);
+    }
+
+    /**
+     * Assert the specified group's integrity. The children count should be >= 0 and each
+     * child should be non-null.
+     *
+     * @param parent The group whose integrity to check
+     */
+    static public void assertGroupIntegrity(ViewGroup parent) {
+        final int count = parent.getChildCount();
+        assertTrue("child count should be >= 0", count >= 0);
+
+        for (int i = 0; i < count; i++) {
+            assertNotNull("group should not contain null children", parent.getChildAt(i));
+            assertSame(parent, parent.getChildAt(i).getParent());
+        }
+    }
+
+    /**
+     * Assert that the specified group contains a specific child once and only once.
+     *
+     * @param parent The group
+     * @param child The child that should belong to group
+     */
+    static public void assertGroupContains(ViewGroup parent, View child) {
+        final int count = parent.getChildCount();
+        assertTrue("Child count should be >= 0", count >= 0);
+
+        boolean found = false;
+        for (int i = 0; i < count; i++) {
+            if (parent.getChildAt(i) == child) {
+                if (!found) {
+                    found = true;
+                } else {
+                    assertTrue("child " + child + " is duplicated in parent", false);
+                }
+            }
+        }
+
+        assertTrue("group does not contain " + child, found);
+    }
+
+    /**
+     * Assert that the specified group does not contain a specific child.
+     *
+     * @param parent The group
+     * @param child The child that should not belong to group
+     */
+    static public void assertGroupNotContains(ViewGroup parent, View child) {
+        final int count = parent.getChildCount();
+        assertTrue("Child count should be >= 0", count >= 0);
+
+        for (int i = 0; i < count; i++) {
+            if (parent.getChildAt(i) == child) {
+                assertTrue("child " + child + " is found in parent", false);
+            }
+        }
+    }
+}
diff --git a/test-runner/android/test/mock/MockApplication.java b/test-runner/android/test/mock/MockApplication.java
new file mode 100644
index 0000000..572dfbf
--- /dev/null
+++ b/test-runner/android/test/mock/MockApplication.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package android.test.mock;
+
+import android.app.Application;
+import android.content.res.Configuration;
+
+/**
+ * A mock {@link android.app.Application} class.  All methods are non-functional and throw 
+ * {@link java.lang.UnsupportedOperationException}.  Override it as necessary to provide the 
+ * operations that you need.
+ */
+public class MockApplication extends Application {
+    
+    public MockApplication() {
+    }
+
+    @Override
+    public void onCreate() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void onTerminate() {
+        throw new UnsupportedOperationException();
+    }
+    
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/test-runner/android/test/mock/MockContentProvider.java b/test-runner/android/test/mock/MockContentProvider.java
new file mode 100644
index 0000000..d04fc44
--- /dev/null
+++ b/test-runner/android/test/mock/MockContentProvider.java
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+package android.test.mock;
+
+import android.content.ContentValues;
+import android.content.IContentProvider;
+import android.content.ISyncAdapter;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.database.CursorWindow;
+import android.database.IBulkCursor;
+import android.database.IContentObserver;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+
+import java.io.FileNotFoundException;
+
+/**
+ * Mock implementation of IContentProvider that does nothing.  All methods are non-functional and 
+ * throw {@link java.lang.UnsupportedOperationException}.  Tests can extend this class to
+ * implement behavior needed for tests.
+ * 
+ * @hide - Because IContentProvider hides bulkQuery(), this doesn't pass through JavaDoc
+ * without generating errors.
+ *
+ */
+public class MockContentProvider implements IContentProvider {
+
+    @SuppressWarnings("unused")
+    public int bulkInsert(Uri url, ContentValues[] initialValues) throws RemoteException {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    @SuppressWarnings("unused")
+    public IBulkCursor bulkQuery(Uri url, String[] projection, String selection,
+            String[] selectionArgs, String sortOrder, IContentObserver observer, 
+            CursorWindow window) throws RemoteException {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    @SuppressWarnings("unused")
+    public int delete(Uri url, String selection, String[] selectionArgs) 
+            throws RemoteException {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    @SuppressWarnings("unused")
+    public ISyncAdapter getSyncAdapter() throws RemoteException {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    @SuppressWarnings("unused")
+    public String getType(Uri url) throws RemoteException {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    @SuppressWarnings("unused")
+    public Uri insert(Uri url, ContentValues initialValues) throws RemoteException {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    @SuppressWarnings("unused")
+    public ParcelFileDescriptor openFile(Uri url, String mode) throws RemoteException,
+            FileNotFoundException {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    @SuppressWarnings("unused")
+    public AssetFileDescriptor openAssetFile(Uri uri, String mode)
+            throws FileNotFoundException {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+    
+    @SuppressWarnings("unused")
+    public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs,
+            String sortOrder) throws RemoteException {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    @SuppressWarnings("unused")
+    public int update(Uri url, ContentValues values, String selection, String[] selectionArgs)
+            throws RemoteException {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    public IBinder asBinder() {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+}
diff --git a/test-runner/android/test/mock/MockContentResolver.java b/test-runner/android/test/mock/MockContentResolver.java
new file mode 100644
index 0000000..3a1dc36c
--- /dev/null
+++ b/test-runner/android/test/mock/MockContentResolver.java
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+package android.test.mock;
+
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.IContentProvider;
+import android.database.ContentObserver;
+import android.net.Uri;
+
+import com.google.android.collect.Maps;
+
+import java.util.Map;
+
+/**
+ * A mock {@link android.content.ContentResolver} class that isolates the test code from the real
+ * content system.  All methods are non-functional and throw
+ * {@link java.lang.UnsupportedOperationException}.  
+ *
+ * <p>This only isolates the test code in ways that have proven useful so far. More should be
+ * added as they become a problem.
+ */
+public class MockContentResolver extends ContentResolver {
+    Map<String, ContentProvider> mProviders;
+
+    public MockContentResolver() {
+        super(null);
+        mProviders = Maps.newHashMap();
+    }
+
+    public void addProvider(String name, ContentProvider provider) {
+        mProviders.put(name, provider);
+    }
+
+    /** @hide */
+    @Override
+    protected IContentProvider acquireProvider(Context context, String name) {
+        final ContentProvider provider = mProviders.get(name);
+        if (provider != null) {
+            return provider.getIContentProvider();
+        } else {
+            return null;
+        }
+    }
+
+    /** @hide */
+    @Override
+    public boolean releaseProvider(IContentProvider provider) {
+        return true;
+    }
+
+    @Override
+    public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
+    }
+}
diff --git a/test-runner/android/test/mock/MockContext.java b/test-runner/android/test/mock/MockContext.java
new file mode 100644
index 0000000..e733dd1
--- /dev/null
+++ b/test-runner/android/test/mock/MockContext.java
@@ -0,0 +1,389 @@
+/*
+ * Copyright (C) 2007 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.test.mock;
+
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.BroadcastReceiver;
+import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.database.sqlite.SQLiteDatabase;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A mock {@link android.content.Context} class.  All methods are non-functional and throw 
+ * {@link java.lang.UnsupportedOperationException}.  You can use this to inject other dependencies,
+ * mocks, or monitors into the classes you are testing.
+ */
+public class MockContext extends Context {
+
+    @Override
+    public AssetManager getAssets() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Resources getResources() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public PackageManager getPackageManager() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public ContentResolver getContentResolver() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Looper getMainLooper() {
+        throw new UnsupportedOperationException();
+    }
+    
+    @Override
+    public Context getApplicationContext() {
+        throw new UnsupportedOperationException();
+    }
+    
+    @Override
+    public void setTheme(int resid) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Resources.Theme getTheme() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public ClassLoader getClassLoader() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getPackageName() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getPackageResourcePath() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getPackageCodePath() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public SharedPreferences getSharedPreferences(String name, int mode) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public FileInputStream openFileInput(String name) throws FileNotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public FileOutputStream openFileOutput(String name, int mode) throws FileNotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean deleteFile(String name) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public File getFileStreamPath(String name) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String[] fileList() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public File getFilesDir() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public File getCacheDir() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public File getDir(String name, int mode) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public SQLiteDatabase openOrCreateDatabase(String file, int mode, 
+            SQLiteDatabase.CursorFactory factory) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public File getDatabasePath(String name) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String[] databaseList() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean deleteDatabase(String name) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Drawable getWallpaper() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Drawable peekWallpaper() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int getWallpaperDesiredMinimumWidth() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int getWallpaperDesiredMinimumHeight() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setWallpaper(Bitmap bitmap) throws IOException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setWallpaper(InputStream data) throws IOException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void clearWallpaper() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void startActivity(Intent intent) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void sendBroadcast(Intent intent) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void sendBroadcast(Intent intent, String receiverPermission) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void sendOrderedBroadcast(Intent intent,
+            String receiverPermission) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void sendOrderedBroadcast(Intent intent, String receiverPermission,
+            BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData,
+           Bundle initialExtras) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void sendStickyBroadcast(Intent intent) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void removeStickyBroadcast(Intent intent) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+            String broadcastPermission, Handler scheduler) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void unregisterReceiver(BroadcastReceiver receiver) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public ComponentName startService(Intent service) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean stopService(Intent service) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean bindService(Intent service, ServiceConnection conn, int flags) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void unbindService(ServiceConnection conn) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean startInstrumentation(ComponentName className,
+            String profileFile, Bundle arguments) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Object getSystemService(String name) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int checkPermission(String permission, int pid, int uid) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int checkCallingPermission(String permission) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int checkCallingOrSelfPermission(String permission) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void enforcePermission(
+            String permission, int pid, int uid, String message) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void enforceCallingPermission(String permission, String message) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void enforceCallingOrSelfPermission(String permission, String message) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void grantUriPermission(String toPackage, Uri uri, int modeFlags) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void revokeUriPermission(Uri uri, int modeFlags) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int checkCallingUriPermission(Uri uri, int modeFlags) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int checkCallingOrSelfUriPermission(Uri uri, int modeFlags) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int checkUriPermission(Uri uri, String readPermission,
+            String writePermission, int pid, int uid, int modeFlags) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void enforceUriPermission(
+            Uri uri, int pid, int uid, int modeFlags, String message) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void enforceCallingUriPermission(
+            Uri uri, int modeFlags, String message) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void enforceCallingOrSelfUriPermission(
+            Uri uri, int modeFlags, String message) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void enforceUriPermission(
+            Uri uri, String readPermission, String writePermission,
+            int pid, int uid, int modeFlags, String message) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Context createPackageContext(String packageName, int flags)
+            throws PackageManager.NameNotFoundException {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/test-runner/android/test/mock/MockDialogInterface.java b/test-runner/android/test/mock/MockDialogInterface.java
new file mode 100644
index 0000000..e4dd0ba
--- /dev/null
+++ b/test-runner/android/test/mock/MockDialogInterface.java
@@ -0,0 +1,20 @@
+// Copyright 2008 The Android Open Source Project
+
+package android.test.mock;
+
+import android.content.DialogInterface;
+
+/**
+ * A mock {@link android.content.DialogInterface} class.  All methods are non-functional and throw 
+ * {@link java.lang.UnsupportedOperationException}. Override it to provide the operations that you 
+ * need.
+ */
+public class MockDialogInterface implements DialogInterface {
+    public void cancel() {
+        throw new UnsupportedOperationException("not implemented yet");
+    }
+
+    public void dismiss() {
+        throw new UnsupportedOperationException("not implemented yet");
+    }
+}
diff --git a/test-runner/android/test/mock/MockPackageManager.java b/test-runner/android/test/mock/MockPackageManager.java
new file mode 100644
index 0000000..ea190e2
--- /dev/null
+++ b/test-runner/android/test/mock/MockPackageManager.java
@@ -0,0 +1,413 @@
+/*
+ * 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.
+ */
+
+package android.test.mock;
+
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageDeleteObserver;
+import android.content.pm.IPackageDataObserver;
+import android.content.pm.IPackageInstallObserver;
+import android.content.pm.IPackageStatsObserver;
+import android.content.pm.InstrumentationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionGroupInfo;
+import android.content.pm.PermissionInfo;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.RemoteException;
+
+import java.util.List;
+
+/**
+ * A mock {@link android.content.pm.PackageManager} class.  All methods are non-functional and throw
+ * {@link java.lang.UnsupportedOperationException}. Override it to provide the operations that you 
+ * need.
+ */
+public class MockPackageManager extends PackageManager {
+
+    @Override
+    public PackageInfo getPackageInfo(String packageName, int flags)
+    throws NameNotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Intent getLaunchIntentForPackage(String packageName)
+            throws NameNotFoundException {
+        throw new UnsupportedOperationException();
+    }
+    
+    @Override
+    public int[] getPackageGids(String packageName) throws NameNotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public PermissionInfo getPermissionInfo(String name, int flags)
+    throws NameNotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public List<PermissionInfo> queryPermissionsByGroup(String group, int flags)
+            throws NameNotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public PermissionGroupInfo getPermissionGroupInfo(String name,
+            int flags) throws NameNotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public List<PermissionGroupInfo> getAllPermissionGroups(int flags) {
+        throw new UnsupportedOperationException();
+    }
+    
+    @Override
+    public ApplicationInfo getApplicationInfo(String packageName, int flags)
+    throws NameNotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public ActivityInfo getActivityInfo(ComponentName className, int flags)
+    throws NameNotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public ActivityInfo getReceiverInfo(ComponentName className, int flags)
+    throws NameNotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public ServiceInfo getServiceInfo(ComponentName className, int flags)
+    throws NameNotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public List<PackageInfo> getInstalledPackages(int flags) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int checkPermission(String permName, String pkgName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean addPermission(PermissionInfo info) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void removePermission(String name) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int checkSignatures(String pkg1, String pkg2) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String[] getPackagesForUid(int uid) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getNameForUid(int uid) {
+        throw new UnsupportedOperationException();
+    }
+    
+    /**
+     * @hide - to match hiding in superclass
+     */
+    @Override
+    public int getUidForSharedUser(String sharedUserName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public List<ApplicationInfo> getInstalledApplications(int flags) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public ResolveInfo resolveActivity(Intent intent, int flags) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public List<ResolveInfo> queryIntentActivityOptions(ComponentName caller,
+            Intent[] specifics, Intent intent, int flags) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public List<ResolveInfo> queryBroadcastReceivers(Intent intent, int flags) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public ResolveInfo resolveService(Intent intent, int flags) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public List<ResolveInfo> queryIntentServices(Intent intent, int flags) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public ProviderInfo resolveContentProvider(String name, int flags) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public List<ProviderInfo> queryContentProviders(String processName, int uid, int flags) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public InstrumentationInfo getInstrumentationInfo(ComponentName className, int flags)
+    throws NameNotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public List<InstrumentationInfo> queryInstrumentation(
+            String targetPackage, int flags) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Drawable getDrawable(String packageName, int resid, ApplicationInfo appInfo) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Drawable getActivityIcon(ComponentName activityName)
+    throws NameNotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Drawable getActivityIcon(Intent intent) throws NameNotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Drawable getDefaultActivityIcon() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Drawable getApplicationIcon(ApplicationInfo info) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Drawable getApplicationIcon(String packageName) throws NameNotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public CharSequence getText(String packageName, int resid, ApplicationInfo appInfo) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public XmlResourceParser getXml(String packageName, int resid,
+            ApplicationInfo appInfo) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public CharSequence getApplicationLabel(ApplicationInfo info) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Resources getResourcesForActivity(ComponentName activityName)
+    throws NameNotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Resources getResourcesForApplication(ApplicationInfo app) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Resources getResourcesForApplication(String appPackageName)
+    throws NameNotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public PackageInfo getPackageArchiveInfo(String archiveFilePath, int flags) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void installPackage(Uri packageURI, IPackageInstallObserver observer,
+            int flags) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @hide - to match hiding in superclass
+     */
+    @Override
+    public void clearApplicationUserData(
+            String packageName, IPackageDataObserver observer) {
+        throw new UnsupportedOperationException();
+    }
+    
+    /**
+     * @hide - to match hiding in superclass
+     */
+    @Override
+    public void deleteApplicationCacheFiles(
+            String packageName, IPackageDataObserver observer) {
+        throw new UnsupportedOperationException();
+    }
+    
+    /**
+     * @hide - to match hiding in superclass
+     */
+    @Override
+    public void freeStorageAndNotify(
+            long idealStorageSize, IPackageDataObserver observer) {
+        throw new UnsupportedOperationException();
+    }
+    
+    /**
+     * @hide - to match hiding in superclass
+     */
+    @Override
+    public void freeStorage(
+            long idealStorageSize, PendingIntent onFinishedIntent) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @hide - to match hiding in superclass
+     */
+    @Override
+    public void deletePackage(
+            String packageName, IPackageDeleteObserver observer, int flags) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void addPackageToPreferred(String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void removePackageFromPreferred(String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public List<PackageInfo> getPreferredPackages(int flags) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setComponentEnabledSetting(ComponentName componentName,
+            int newState, int flags) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int getComponentEnabledSetting(ComponentName componentName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setApplicationEnabledSetting(String packageName, int newState, int flags) {
+        throw new UnsupportedOperationException();
+    }
+    
+    @Override
+    public int getApplicationEnabledSetting(String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void addPreferredActivity(IntentFilter filter,
+            int match, ComponentName[] set, ComponentName activity) {
+        throw new UnsupportedOperationException();
+    }
+    
+    @Override
+    public void clearPackagePreferredActivities(String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @hide - to match hiding in superclass
+     */
+    @Override
+    public void getPackageSizeInfo(String packageName, IPackageStatsObserver observer) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void installPackage(Uri packageURI) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int getPreferredActivities(List<IntentFilter> outFilters,
+            List<ComponentName> outActivities, String packageName) {
+        throw new UnsupportedOperationException();
+    }
+    
+    @Override
+    public String[] getSystemSharedLibraryNames() {
+        throw new UnsupportedOperationException();
+    }
+    
+    @Override
+    public boolean isSafeMode() {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/test-runner/android/test/mock/MockResources.java b/test-runner/android/test/mock/MockResources.java
new file mode 100644
index 0000000..18752ce
--- /dev/null
+++ b/test-runner/android/test/mock/MockResources.java
@@ -0,0 +1,222 @@
+/*
+ * 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.
+ */
+
+package android.test.mock;
+
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.content.res.ColorStateList;
+import android.content.res.XmlResourceParser;
+import android.content.res.AssetFileDescriptor;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.util.AttributeSet;
+import android.graphics.drawable.Drawable;
+import android.graphics.Movie;
+
+import java.io.InputStream;
+
+/**
+ * A mock {@link android.content.res.Resources} class.  All methods are non-functional and throw 
+ * {@link java.lang.UnsupportedOperationException}. Override it to provide the operations that you 
+ * need.
+ */
+public class MockResources extends Resources {
+
+    public MockResources() {
+        super(new AssetManager(), null, null);
+    }
+
+    @Override
+    public void updateConfiguration(Configuration config, DisplayMetrics metrics) {
+        // this method is called from the constructor, so we just do nothing
+    }
+
+    @Override
+    public CharSequence getText(int id) throws NotFoundException {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public CharSequence getQuantityText(int id, int quantity) throws NotFoundException {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public String getString(int id) throws NotFoundException {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public String getString(int id, Object... formatArgs) throws NotFoundException {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public String getQuantityString(int id, int quantity, Object... formatArgs)
+            throws NotFoundException {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public String getQuantityString(int id, int quantity) throws NotFoundException {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public CharSequence getText(int id, CharSequence def) {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public CharSequence[] getTextArray(int id) throws NotFoundException {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public String[] getStringArray(int id) throws NotFoundException {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public int[] getIntArray(int id) throws NotFoundException {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public TypedArray obtainTypedArray(int id) throws NotFoundException {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public float getDimension(int id) throws NotFoundException {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public int getDimensionPixelOffset(int id) throws NotFoundException {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public int getDimensionPixelSize(int id) throws NotFoundException {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public Drawable getDrawable(int id) throws NotFoundException {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public Movie getMovie(int id) throws NotFoundException {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public int getColor(int id) throws NotFoundException {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public ColorStateList getColorStateList(int id) throws NotFoundException {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public int getInteger(int id) throws NotFoundException {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public XmlResourceParser getLayout(int id) throws NotFoundException {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public XmlResourceParser getAnimation(int id) throws NotFoundException {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public XmlResourceParser getXml(int id) throws NotFoundException {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public InputStream openRawResource(int id) throws NotFoundException {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public AssetFileDescriptor openRawResourceFd(int id) throws NotFoundException {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public void getValue(int id, TypedValue outValue, boolean resolveRefs)
+            throws NotFoundException {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public void getValue(String name, TypedValue outValue, boolean resolveRefs)
+            throws NotFoundException {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public TypedArray obtainAttributes(AttributeSet set, int[] attrs) {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public DisplayMetrics getDisplayMetrics() {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public Configuration getConfiguration() {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public int getIdentifier(String name, String defType, String defPackage) {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public String getResourceName(int resid) throws NotFoundException {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public String getResourcePackageName(int resid) throws NotFoundException {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public String getResourceTypeName(int resid) throws NotFoundException {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+
+    @Override
+    public String getResourceEntryName(int resid) throws NotFoundException {
+        throw new UnsupportedOperationException("mock object, not implemented");
+    }
+}
diff --git a/test-runner/android/test/mock/package.html b/test-runner/android/test/mock/package.html
new file mode 100644
index 0000000..0f1bc6f
--- /dev/null
+++ b/test-runner/android/test/mock/package.html
@@ -0,0 +1,5 @@
+<HTML>
+<BODY>
+Utility classes providing stubs or mocks of various Android framework building blocks.
+</BODY>
+</HTML>
diff --git a/test-runner/android/test/suitebuilder/AssignableFrom.java b/test-runner/android/test/suitebuilder/AssignableFrom.java
new file mode 100644
index 0000000..38b4ee3
--- /dev/null
+++ b/test-runner/android/test/suitebuilder/AssignableFrom.java
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+package android.test.suitebuilder;
+
+import com.android.internal.util.Predicate;
+
+class AssignableFrom implements Predicate<TestMethod> {
+
+    private final Class root;
+
+    AssignableFrom(Class root) {
+        this.root = root;
+    }
+
+    public boolean apply(TestMethod testMethod) {
+        return root.isAssignableFrom(testMethod.getEnclosingClass());
+    }
+}
diff --git a/test-runner/android/test/suitebuilder/InstrumentationTestSuiteBuilder.java b/test-runner/android/test/suitebuilder/InstrumentationTestSuiteBuilder.java
new file mode 100644
index 0000000..128396e
--- /dev/null
+++ b/test-runner/android/test/suitebuilder/InstrumentationTestSuiteBuilder.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package android.test.suitebuilder;
+
+/**
+ * A suite builder that finds instrumentation tests.
+ * 
+ * {@hide} Not needed for 1.0 SDK.
+ */
+public class InstrumentationTestSuiteBuilder extends TestSuiteBuilder {
+
+    public InstrumentationTestSuiteBuilder(Class clazz) {
+        this(clazz.getName(), clazz.getClassLoader());
+    }
+
+
+    public InstrumentationTestSuiteBuilder(String name, ClassLoader classLoader) {
+        super(name, classLoader);
+        addRequirements(TestPredicates.SELECT_INSTRUMENTATION);
+    }
+}
diff --git a/test-runner/android/test/suitebuilder/SmokeTestSuiteBuilder.java b/test-runner/android/test/suitebuilder/SmokeTestSuiteBuilder.java
new file mode 100644
index 0000000..01e7ec6
--- /dev/null
+++ b/test-runner/android/test/suitebuilder/SmokeTestSuiteBuilder.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package android.test.suitebuilder;
+
+/**
+ * A suite builder that runs smoke tests.
+ * 
+ * {@hide} Not needed for 1.0 SDK.
+ */
+public class SmokeTestSuiteBuilder extends TestSuiteBuilder {
+
+    public SmokeTestSuiteBuilder(Class clazz) {
+        this(clazz.getName(), clazz.getClassLoader());
+    }
+
+
+    public SmokeTestSuiteBuilder(String name, ClassLoader classLoader) {
+        super(name, classLoader);
+        addRequirements(TestPredicates.SELECT_SMOKE);
+    }
+}
diff --git a/test-runner/android/test/suitebuilder/TestGrouping.java b/test-runner/android/test/suitebuilder/TestGrouping.java
new file mode 100644
index 0000000..df6da70
--- /dev/null
+++ b/test-runner/android/test/suitebuilder/TestGrouping.java
@@ -0,0 +1,249 @@
+/*
+ * 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.
+ */
+
+package android.test.suitebuilder;
+
+import android.test.ClassPathPackageInfo;
+import android.test.ClassPathPackageInfoSource;
+import android.test.PackageInfoSources;
+import android.util.Log;
+import com.android.internal.util.Predicate;
+import junit.framework.TestCase;
+
+import java.io.Serializable;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * Represents a collection of test classes present on the classpath. You can add individual classes
+ * or entire packages. By default sub-packages are included recursively, but methods are
+ * provided to allow for arbitrary inclusion or exclusion of sub-packages. Typically a
+ * {@link TestGrouping} will have only one root package, but this is not a requirement.
+ * 
+ * {@hide} Not needed for 1.0 SDK.
+ */
+public class TestGrouping {
+
+    SortedSet<Class<? extends TestCase>> testCaseClasses;
+
+    public static final Comparator<Class<? extends TestCase>> SORT_BY_SIMPLE_NAME
+            = new SortBySimpleName();
+
+    public static final Comparator<Class<? extends TestCase>> SORT_BY_FULLY_QUALIFIED_NAME
+            = new SortByFullyQualifiedName();
+
+    protected String firstIncludedPackage = null;
+    private ClassLoader classLoader;
+
+    public TestGrouping(Comparator<Class<? extends TestCase>> comparator) {
+        testCaseClasses = new TreeSet<Class<? extends TestCase>>(comparator);
+    }
+
+    /**
+     * @return A list of all tests in the package, including small, medium, large,
+     *         flaky, and suppressed tests. Includes sub-packages recursively.
+     */
+    public List<TestMethod> getTests() {
+        List<TestMethod> testMethods = new ArrayList<TestMethod>();
+        for (Class<? extends TestCase> testCase : testCaseClasses) {
+            for (Method testMethod : getTestMethods(testCase)) {
+                testMethods.add(new TestMethod(testMethod, testCase));
+            }
+        }
+        return testMethods;
+    }
+
+    protected List<Method> getTestMethods(Class<? extends TestCase> testCaseClass) {
+        List<Method> methods = Arrays.asList(testCaseClass.getMethods());
+        return select(methods, new TestMethodPredicate());
+    }
+
+    SortedSet<Class<? extends TestCase>> getTestCaseClasses() {
+        return testCaseClasses;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        TestGrouping other = (TestGrouping) o;
+        if (!this.testCaseClasses.equals(other.testCaseClasses)) {
+            return false;
+        }
+        return this.testCaseClasses.comparator().equals(other.testCaseClasses.comparator());
+    }
+
+    public int hashCode() {
+        return testCaseClasses.hashCode();
+    }
+
+    /**
+     * Include all tests in the given packages and all their sub-packages, unless otherwise
+     * specified. Each of the given packages must contain at least one test class, either directly
+     * or in a sub-package.
+     *
+     * @param packageNames Names of packages to add.
+     * @return The {@link TestGrouping} for method chaining.
+     */
+    public TestGrouping addPackagesRecursive(String... packageNames) {
+        for (String packageName : packageNames) {
+            List<Class<? extends TestCase>> addedClasses = testCaseClassesInPackage(packageName);
+            if (addedClasses.isEmpty()) {
+                Log.w("TestGrouping", "Invalid Package: '" + packageName
+                        + "' could not be found or has no tests");
+            }
+            testCaseClasses.addAll(addedClasses);
+            if (firstIncludedPackage == null) {
+                firstIncludedPackage = packageName;
+            }
+        }
+        return this;
+    }
+
+    /**
+     * Exclude all tests in the given packages and all their sub-packages, unless otherwise
+     * specified.
+     *
+     * @param packageNames Names of packages to remove.
+     * @return The {@link TestGrouping} for method chaining.
+     */
+    public TestGrouping removePackagesRecursive(String... packageNames) {
+        for (String packageName : packageNames) {
+            testCaseClasses.removeAll(testCaseClassesInPackage(packageName));
+        }
+        return this;
+    }
+
+    /**
+     * @return The first package name passed to {@link #addPackagesRecursive(String[])}, or null
+     *         if that method was never called.
+     */
+    public String getFirstIncludedPackage() {
+        return firstIncludedPackage;
+    }
+
+    private List<Class<? extends TestCase>> testCaseClassesInPackage(String packageName) {
+        ClassPathPackageInfoSource source = PackageInfoSources.forClassPath(classLoader);
+        ClassPathPackageInfo packageInfo = source.getPackageInfo(packageName);
+
+        return selectTestClasses(packageInfo.getTopLevelClassesRecursive());
+    }
+
+    @SuppressWarnings("unchecked")
+    private List<Class<? extends TestCase>> selectTestClasses(Set<Class<?>> allClasses) {
+        List<Class<? extends TestCase>> testClasses = new ArrayList<Class<? extends TestCase>>();
+        for (Class<?> testClass : select(allClasses,
+                new TestCasePredicate())) {
+            testClasses.add((Class<? extends TestCase>) testClass);
+        }
+        return testClasses;
+    }
+
+    private <T> List<T> select(Collection<T> items, Predicate<T> predicate) {
+        ArrayList<T> selectedItems = new ArrayList<T>();
+        for (T item : items) {
+            if (predicate.apply(item)) {
+                selectedItems.add(item);
+            }
+        }
+        return selectedItems;
+    }
+
+    public void setClassLoader(ClassLoader classLoader) {
+        this.classLoader = classLoader;
+    }
+
+    /**
+     * Sort classes by their simple names (i.e. without the package prefix), using
+     * their packages to sort classes with the same name.
+     */
+    private static class SortBySimpleName
+            implements Comparator<Class<? extends TestCase>>, Serializable {
+
+        public int compare(Class<? extends TestCase> class1,
+                Class<? extends TestCase> class2) {
+            int result = class1.getSimpleName().compareTo(class2.getSimpleName());
+            if (result != 0) {
+                return result;
+            }
+            return class1.getName().compareTo(class2.getName());
+        }
+    }
+
+    /**
+     * Sort classes by their fully qualified names (i.e. with the package
+     * prefix).
+     */
+    private static class SortByFullyQualifiedName
+            implements Comparator<Class<? extends TestCase>>, Serializable {
+
+        public int compare(Class<? extends TestCase> class1,
+                Class<? extends TestCase> class2) {
+            return class1.getName().compareTo(class2.getName());
+        }
+    }
+
+    private static class TestCasePredicate implements Predicate<Class<?>> {
+
+        public boolean apply(Class aClass) {
+            int modifiers = ((Class<?>) aClass).getModifiers();
+            return TestCase.class.isAssignableFrom((Class<?>) aClass)
+                    && Modifier.isPublic(modifiers)
+                    && !Modifier.isAbstract(modifiers)
+                    && hasValidConstructor((Class<?>) aClass);
+        }
+
+        @SuppressWarnings("unchecked")
+        private boolean hasValidConstructor(java.lang.Class<?> aClass) {
+            // The cast below is not necessary with the Java 5 compiler, but necessary with the Java 6 compiler,
+            // where the return type of Class.getDeclaredConstructors() was changed
+            // from Constructor<T>[] to Constructor<?>[]
+            Constructor<? extends TestCase>[] constructors
+                    = (Constructor<? extends TestCase>[]) aClass.getConstructors();
+            for (Constructor<? extends TestCase> constructor : constructors) {
+                if (Modifier.isPublic(constructor.getModifiers())) {
+                    java.lang.Class[] parameterTypes = constructor.getParameterTypes();
+                    if (parameterTypes.length == 0 ||
+                            (parameterTypes.length == 1 && parameterTypes[0] == String.class)) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+    }
+
+    private static class TestMethodPredicate implements Predicate<Method> {
+
+        public boolean apply(Method method) {
+            return ((method.getParameterTypes().length == 0) &&
+                    (method.getName().startsWith("test")) &&
+                    (method.getReturnType().getSimpleName().equals("void")));
+        }
+    }
+}
diff --git a/test-runner/android/test/suitebuilder/TestMethod.java b/test-runner/android/test/suitebuilder/TestMethod.java
new file mode 100644
index 0000000..08568d5
--- /dev/null
+++ b/test-runner/android/test/suitebuilder/TestMethod.java
@@ -0,0 +1,146 @@
+/*
+ * 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.
+ */
+
+package android.test.suitebuilder;
+
+import junit.framework.TestCase;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Represents a test to be run. Can be constructed without instantiating the TestCase or even
+ * loading the class.
+ */
+public class TestMethod {
+
+    private final String enclosingClassname;
+    private final String testMethodName;
+    private final Class<? extends TestCase> enclosingClass;
+
+    public TestMethod(Method method, Class<? extends TestCase> enclosingClass) {
+        this(method.getName(), enclosingClass);
+    }
+
+    public TestMethod(String methodName, Class<? extends TestCase> enclosingClass) {
+        this.enclosingClass = enclosingClass;
+        this.enclosingClassname = enclosingClass.getName();
+        this.testMethodName = methodName;
+    }
+    
+    public TestMethod(TestCase testCase) {
+        this(testCase.getName(), testCase.getClass());
+    }
+
+    public String getName() {
+        return testMethodName;
+    }
+
+    public String getEnclosingClassname() {
+        return enclosingClassname;
+    }
+
+    public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
+        try {
+            return getEnclosingClass().getMethod(getName()).getAnnotation(annotationClass);
+        } catch (NoSuchMethodException e) {
+            return null;
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public Class<? extends TestCase> getEnclosingClass() {
+        return enclosingClass;
+    }
+
+    public TestCase createTest()
+            throws InvocationTargetException, IllegalAccessException, InstantiationException {
+        return instantiateTest(enclosingClass, testMethodName);
+    }
+
+    @SuppressWarnings("unchecked")
+    private TestCase instantiateTest(Class testCaseClass, String testName)
+            throws InvocationTargetException, IllegalAccessException, InstantiationException {
+        Constructor[] constructors = testCaseClass.getConstructors();
+
+        if (constructors.length == 0) {
+            return instantiateTest(testCaseClass.getSuperclass(), testName);
+        } else {
+            for (Constructor constructor : constructors) {
+                Class[] params = constructor.getParameterTypes();
+                if (noargsConstructor(params)) {
+                    TestCase test = ((Constructor<? extends TestCase>) constructor).newInstance();
+                    // JUnit will run just the one test if you call
+                    // {@link TestCase#setName(String)}
+                    test.setName(testName);
+                    return test;
+                } else if (singleStringConstructor(params)) {
+                    return ((Constructor<? extends TestCase>) constructor)
+                            .newInstance(testName);
+                }
+            }
+        }
+        throw new RuntimeException("Unable to locate a constructor for "
+                + testCaseClass.getName());
+    }
+
+    private boolean singleStringConstructor(Class[] params) {
+        return (params.length == 1) && (params[0].equals(String.class));
+    }
+
+    private boolean noargsConstructor(Class[] params) {
+        return params.length == 0;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        TestMethod that = (TestMethod) o;
+
+        if (enclosingClassname != null
+                ? !enclosingClassname.equals(that.enclosingClassname)
+                : that.enclosingClassname != null) {
+            return false;
+        }
+        if (testMethodName != null
+                ? !testMethodName.equals(that.testMethodName)
+                : that.testMethodName != null) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result;
+        result = (enclosingClassname != null ? enclosingClassname.hashCode() : 0);
+        result = 31 * result + (testMethodName != null ? testMethodName.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return enclosingClassname + "." + testMethodName;
+    }
+}
diff --git a/test-runner/android/test/suitebuilder/TestPredicates.java b/test-runner/android/test/suitebuilder/TestPredicates.java
new file mode 100644
index 0000000..d814e0b
--- /dev/null
+++ b/test-runner/android/test/suitebuilder/TestPredicates.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package android.test.suitebuilder;
+
+import android.test.InstrumentationTestCase;
+import android.test.PerformanceTestBase;
+import android.test.suitebuilder.annotation.HasAnnotation;
+import android.test.suitebuilder.annotation.Suppress;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Smoke;
+import com.android.internal.util.Predicate;
+import com.android.internal.util.Predicates;
+
+/**
+ * {@hide} Not needed for 1.0 SDK.
+ */
+public class TestPredicates {
+
+    public static final Predicate<TestMethod> SELECT_INSTRUMENTATION =
+            new AssignableFrom(InstrumentationTestCase.class);
+    public static final Predicate<TestMethod> REJECT_INSTRUMENTATION =
+            Predicates.not(SELECT_INSTRUMENTATION);
+
+    public static final Predicate<TestMethod> SELECT_SMOKE = new HasAnnotation(Smoke.class);
+    public static final Predicate<TestMethod> SELECT_SMALL = new HasAnnotation(SmallTest.class);
+    public static final Predicate<TestMethod> SELECT_MEDIUM = new HasAnnotation(MediumTest.class);
+    public static final Predicate<TestMethod> SELECT_LARGE = new HasAnnotation(LargeTest.class);
+    public static final Predicate<TestMethod> REJECT_SUPPRESSED =
+            Predicates.not(new HasAnnotation(Suppress.class));
+    public static final Predicate<TestMethod> REJECT_PERFORMANCE =
+            Predicates.not(new AssignableFrom(PerformanceTestBase.class));
+
+}
diff --git a/test-runner/android/test/suitebuilder/TestSuiteBuilder.java b/test-runner/android/test/suitebuilder/TestSuiteBuilder.java
new file mode 100644
index 0000000..428905e
--- /dev/null
+++ b/test-runner/android/test/suitebuilder/TestSuiteBuilder.java
@@ -0,0 +1,282 @@
+/*
+ * 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.
+ */
+
+package android.test.suitebuilder;
+
+import android.content.Context;
+import android.test.AndroidTestRunner;
+import android.test.TestCaseUtil;
+import android.util.Log;
+import com.android.internal.util.Predicate;
+import com.google.android.collect.Lists;
+import static android.test.suitebuilder.TestGrouping.SORT_BY_FULLY_QUALIFIED_NAME;
+import static android.test.suitebuilder.TestPredicates.REJECT_SUPPRESSED;
+import static android.test.suitebuilder.TestPredicates.REJECT_PERFORMANCE;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * Build suites based on a combination of included packages, excluded packages,
+ * and predicates that must be satisfied.
+ */
+public class TestSuiteBuilder {
+
+    private Context context;
+    private final TestGrouping testGrouping = new TestGrouping(SORT_BY_FULLY_QUALIFIED_NAME);
+    private final Set<Predicate<TestMethod>> predicates = new HashSet<Predicate<TestMethod>>();
+    private List<TestCase> testCases;
+    private TestSuite rootSuite;
+    private TestSuite suiteForCurrentClass;
+    private String currentClassname;
+    private String suiteName;
+
+    /**
+     * The given name is automatically prefixed with the package containing the tests to be run.
+     * If more than one package is specified, the first is used.
+     *
+     * @param clazz Use the class from your .apk. Use the class name for the test suite name.
+     *              Use the class' classloader in order to load classes for testing.
+     *              This is needed when running in the emulator.
+     */
+    public TestSuiteBuilder(Class clazz) {
+        this(clazz.getName(), clazz.getClassLoader());
+    }
+
+    public TestSuiteBuilder(String name, ClassLoader classLoader) {
+        this.suiteName = name;
+        this.testGrouping.setClassLoader(classLoader);
+        this.testCases = Lists.newArrayList();
+        addRequirements(REJECT_SUPPRESSED);
+    }
+    
+    /** @hide pending API Council approval */
+    public TestSuiteBuilder addTestClassByName(String testClassName, String testMethodName, 
+            Context context) {
+
+        AndroidTestRunner atr = new AndroidTestRunner();
+        atr.setContext(context);
+        atr.setTestClassName(testClassName, testMethodName);
+
+        this.testCases.addAll(atr.getTestCases());
+        return this;
+    }
+    
+    /** @hide pending API Council approval */
+    public TestSuiteBuilder addTestSuite(TestSuite testSuite) {
+        for (TestCase testCase : (List<TestCase>) TestCaseUtil.getTests(testSuite, true)) {
+            this.testCases.add(testCase);
+        }
+        return this;
+    }
+
+    /**
+     * Include all tests that satisfy the requirements in the given packages and all sub-packages,
+     * unless otherwise specified.
+     *
+     * @param packageNames Names of packages to add.
+     * @return The builder for method chaining.
+     */
+    public TestSuiteBuilder includePackages(String... packageNames) {
+        testGrouping.addPackagesRecursive(packageNames);
+        return this;
+    }
+
+    /**
+     * Exclude all tests in the given packages and all sub-packages, unless otherwise specified.
+     *
+     * @param packageNames Names of packages to remove.
+     * @return The builder for method chaining.
+     */
+    public TestSuiteBuilder excludePackages(String... packageNames) {
+        testGrouping.removePackagesRecursive(packageNames);
+        return this;
+    }
+
+    /**
+     * Exclude tests that fail to satisfy all of the given predicates.
+     *
+     * @param predicates Predicates to add to the list of requirements.
+     * @return The builder for method chaining.
+     */
+    public TestSuiteBuilder addRequirements(List<Predicate<TestMethod>> predicates) {
+        this.predicates.addAll(predicates);
+        return this;
+    }
+
+    /**
+     * Include all junit tests that satisfy the requirements in the calling class' package and all
+     * sub-packages.
+     *
+     * @return The builder for method chaining.
+     */
+    public final TestSuiteBuilder includeAllPackagesUnderHere() {
+        StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
+
+        String callingClassName = null;
+        String thisClassName = TestSuiteBuilder.class.getName();
+
+        // We want to get the package of this method's calling class. This method's calling class
+        // should be one level below this class in the stack trace.
+        for (int i = 0; i < stackTraceElements.length; i++) {
+            StackTraceElement element = stackTraceElements[i];
+            if (thisClassName.equals(element.getClassName())
+                    && "includeAllPackagesUnderHere".equals(element.getMethodName())) {
+                // We've found this class in the call stack. The calling class must be the
+                // next class in the stack.
+                callingClassName = stackTraceElements[i + 1].getClassName();
+                break;
+            }
+        }
+
+        String packageName = parsePackageNameFromClassName(callingClassName);
+        return includePackages(packageName);
+    }
+
+    /**
+     * Override the default name for the suite being built. This should generally be called if you
+     * call {@link #addRequirements(com.android.internal.util.Predicate[])} to make it clear which
+     * tests will be included. The name you specify is automatically prefixed with the package
+     * containing the tests to be run. If more than one package is specified, the first is used.
+     *
+     * @param newSuiteName Prefix of name to give the suite being built.
+     * @return The builder for method chaining.
+     */
+    public TestSuiteBuilder named(String newSuiteName) {
+        suiteName = newSuiteName;
+        return this;
+    }
+
+    /**
+     * Call this method once you've configured your builder as desired.
+     *
+     * @return The suite containing the requested tests.
+     */
+    public final TestSuite build() {
+        rootSuite = new TestSuite(getSuiteName());
+
+        // Keep track of current class so we know when to create a new sub-suite.
+        currentClassname = null;
+        try {
+            for (TestMethod test : testGrouping.getTests()) {
+                if (satisfiesAllPredicates(test)) {
+                    addTest(test);
+                }
+            }
+            if (testCases.size() > 0) {
+                for (TestCase testCase : testCases) {
+                    if (satisfiesAllPredicates(new TestMethod(testCase))) {
+                        addTest(testCase);
+                    }
+                }
+            }
+        } catch (Exception exception) {
+            Log.i("TestSuiteBuilder", "Failed to create test.", exception);
+            TestSuite suite = new TestSuite(getSuiteName());
+            suite.addTest(new FailedToCreateTests(exception));
+            return suite;
+        }
+        return rootSuite;
+    }
+
+    /**
+     * Subclasses use this method to determine the name of the suite.
+     *
+     * @return The package and suite name combined.
+     */
+    protected String getSuiteName() {
+        return suiteName;
+    }
+
+    /**
+     * Exclude tests that fail to satisfy all of the given predicates. If you call this method, you
+     * probably also want to call {@link #named(String)} to override the default suite name.
+     *
+     * @param predicates Predicates to add to the list of requirements.
+     * @return The builder for method chaining.
+     */
+    public final TestSuiteBuilder addRequirements(Predicate<TestMethod>... predicates) {
+        ArrayList<Predicate<TestMethod>> list = new ArrayList<Predicate<TestMethod>>();
+        Collections.addAll(list, predicates);
+        return addRequirements(list);
+    }
+
+    /**
+     * A special {@link junit.framework.TestCase} used to indicate a failure during the build()
+     * step.
+     */
+    public static class FailedToCreateTests extends TestCase {
+        private final Exception exception;
+
+        public FailedToCreateTests(Exception exception) {
+            super("testSuiteConstructionFailed");
+            this.exception = exception;
+        }
+
+        public void testSuiteConstructionFailed() {
+            throw new RuntimeException("Exception during suite construction", exception);
+        }
+    }
+
+    /**
+     * @return the test package that represents the packages that were included for our test suite.
+     * 
+     * {@hide} Not needed for 1.0 SDK.
+     */
+    protected TestGrouping getTestGrouping() {
+        return testGrouping;
+    }
+
+    private boolean satisfiesAllPredicates(TestMethod test) {
+        for (Predicate<TestMethod> predicate : predicates) {
+            if (!predicate.apply(test)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private void addTest(TestMethod testMethod) throws Exception {
+        addSuiteIfNecessary(testMethod.getEnclosingClassname());
+        suiteForCurrentClass.addTest(testMethod.createTest());
+    }
+    
+    private void addTest(Test test) {
+        addSuiteIfNecessary(test.getClass().getName());
+        suiteForCurrentClass.addTest(test);
+    }
+
+    private void addSuiteIfNecessary(String parentClassname) {
+        if (!parentClassname.equals(currentClassname)) {
+            currentClassname = parentClassname;
+            suiteForCurrentClass = new TestSuite(parentClassname);
+            rootSuite.addTest(suiteForCurrentClass);
+        }
+    }
+
+    private static String parsePackageNameFromClassName(String className) {
+        return className.substring(0, className.lastIndexOf('.'));
+    }
+}
diff --git a/test-runner/android/test/suitebuilder/UnitTestSuiteBuilder.java b/test-runner/android/test/suitebuilder/UnitTestSuiteBuilder.java
new file mode 100644
index 0000000..8cf4c86
--- /dev/null
+++ b/test-runner/android/test/suitebuilder/UnitTestSuiteBuilder.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package android.test.suitebuilder;
+
+/**
+ * A suite builder that finds unit tests.
+ * 
+ * {@hide} Not needed for 1.0 SDK.
+ */
+public class UnitTestSuiteBuilder extends TestSuiteBuilder {
+
+    public UnitTestSuiteBuilder(Class clazz) {
+        this(clazz.getName(), clazz.getClassLoader());
+    }
+
+
+    public UnitTestSuiteBuilder(String name, ClassLoader classLoader) {
+        super(name, classLoader);
+        addRequirements(TestPredicates.REJECT_INSTRUMENTATION);
+        addRequirements(TestPredicates.REJECT_PERFORMANCE);
+    }
+}
diff --git a/test-runner/android/test/suitebuilder/annotation/HasAnnotation.java b/test-runner/android/test/suitebuilder/annotation/HasAnnotation.java
new file mode 100644
index 0000000..a2868fc
--- /dev/null
+++ b/test-runner/android/test/suitebuilder/annotation/HasAnnotation.java
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+package android.test.suitebuilder.annotation;
+
+import static com.android.internal.util.Predicates.or;
+import com.android.internal.util.Predicate;
+import android.test.suitebuilder.TestMethod;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * A predicate that checks to see if a {@link TestMethod} has a specific annotation, either on the
+ * method or on the containing class.
+ * 
+ * {@hide} Not needed for 1.0 SDK.
+ */
+public class HasAnnotation implements Predicate<TestMethod> {
+
+    private Predicate<TestMethod> hasMethodOrClassAnnotation;
+
+    public HasAnnotation(Class<? extends Annotation> annotationClass) {
+        this.hasMethodOrClassAnnotation = or(
+                new HasMethodAnnotation(annotationClass),
+                new HasClassAnnotation(annotationClass));
+    }
+
+    public boolean apply(TestMethod testMethod) {
+        return hasMethodOrClassAnnotation.apply(testMethod);
+    }
+}
diff --git a/test-runner/android/test/suitebuilder/annotation/HasClassAnnotation.java b/test-runner/android/test/suitebuilder/annotation/HasClassAnnotation.java
new file mode 100644
index 0000000..ac76f4c
--- /dev/null
+++ b/test-runner/android/test/suitebuilder/annotation/HasClassAnnotation.java
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+package android.test.suitebuilder.annotation;
+
+import java.lang.annotation.Annotation;
+
+import android.test.suitebuilder.TestMethod;
+import com.android.internal.util.Predicate;
+
+/**
+ * A predicate that checks to see if a {@link android.test.suitebuilder.TestMethod} has a specific annotation on the
+ * containing class. Consider using the public {@link HasAnnotation} class instead of this class.
+ * 
+ * {@hide} Not needed for 1.0 SDK.
+ */
+class HasClassAnnotation implements Predicate<TestMethod> {
+
+    private Class<? extends Annotation> annotationClass;
+
+    public HasClassAnnotation(Class<? extends Annotation> annotationClass) {
+        this.annotationClass = annotationClass;
+    }
+
+    public boolean apply(TestMethod testMethod) {
+        return testMethod.getEnclosingClass().getAnnotation(annotationClass) != null;
+    }
+}
diff --git a/test-runner/android/test/suitebuilder/annotation/HasMethodAnnotation.java b/test-runner/android/test/suitebuilder/annotation/HasMethodAnnotation.java
new file mode 100644
index 0000000..96bd922
--- /dev/null
+++ b/test-runner/android/test/suitebuilder/annotation/HasMethodAnnotation.java
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+package android.test.suitebuilder.annotation;
+
+import com.android.internal.util.Predicate;
+import android.test.suitebuilder.TestMethod;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * A predicate that checks to see if a the method represented by {@link TestMethod} has a certain
+ * annotation on it. Consider using the public {@link HasAnnotation} class instead of this class.
+ * 
+ * {@hide} Not needed for 1.0 SDK.
+ */
+class HasMethodAnnotation implements Predicate<TestMethod> {
+
+    private final Class<? extends Annotation> annotationClass;
+
+    public HasMethodAnnotation(Class<? extends Annotation> annotationClass) {
+        this.annotationClass = annotationClass;
+    }
+
+    public boolean apply(TestMethod testMethod) {
+        return testMethod.getAnnotation(annotationClass) != null;
+    }
+}
diff --git a/test-runner/android/test/suitebuilder/annotation/package.html b/test-runner/android/test/suitebuilder/annotation/package.html
new file mode 100644
index 0000000..ffba2e9
--- /dev/null
+++ b/test-runner/android/test/suitebuilder/annotation/package.html
@@ -0,0 +1,5 @@
+<HTML>
+<BODY>
+Utility classes supporting the test runner classes.
+</BODY>
+</HTML>
diff --git a/test-runner/android/test/suitebuilder/package.html b/test-runner/android/test/suitebuilder/package.html
new file mode 100644
index 0000000..ffba2e9
--- /dev/null
+++ b/test-runner/android/test/suitebuilder/package.html
@@ -0,0 +1,5 @@
+<HTML>
+<BODY>
+Utility classes supporting the test runner classes.
+</BODY>
+</HTML>