am 8af013a3: am 77eb7290: am 72604e38: am bd914e49: am e78365c9: Try reading every file on /sys

# Via Android Git Automerger (4) and Nick Kralevich (1)
* commit '8af013a3b91a2dedf6f862525a079ea6c8ec6ddf':
  Try reading every file on /sys
diff --git a/Android.mk b/Android.mk
index 4251262..dbcc97b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -14,6 +14,7 @@
 # limitations under the License.
 #
 
+include cts/suite/pts/PtsBenchmarkingList.mk
 include cts/CtsBuild.mk
 include cts/CtsCoverage.mk
 include $(call all-subdir-makefiles)
diff --git a/CtsBuild.mk b/CtsBuild.mk
index d396006..6e57086 100644
--- a/CtsBuild.mk
+++ b/CtsBuild.mk
@@ -36,6 +36,10 @@
 	$(foreach lib,$(1),$(HOST_OUT_JAVA_LIBRARIES)/$(lib).jar)
 endef
 
+define cts-get-ui-lib-paths
+	$(foreach lib,$(1),$(CTS_TESTCASES_OUT)/$(lib).jar)
+endef
+
 define cts-get-native-paths
 	$(foreach exe,$(1),$(call intermediates-dir-for,EXECUTABLES,$(exe))/$(exe))
 endef
diff --git a/CtsCoverage.mk b/CtsCoverage.mk
index 61ad9c9..8d67a99 100644
--- a/CtsCoverage.mk
+++ b/CtsCoverage.mk
@@ -24,7 +24,7 @@
 
 coverage_out := $(HOST_OUT)/cts-api-coverage
 
-api_text_description := $(SRC_API_DIR)/current.txt
+api_text_description := frameworks/base/api/current.txt
 api_xml_description := $(coverage_out)/api.xml
 $(api_xml_description) : $(api_text_description) $(APICHECK)
 	$(hide) echo "Converting API file to XML: $@"
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index 5511ebd..1e794f9 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -29,6 +29,7 @@
 	CtsMultiUserStorageApp
 
 cts_support_packages := \
+	$(PTS_SUPPORT_PACKAGES) \
 	CtsAccelerationTestStubs \
 	CtsDelegatingAccessibilityService \
 	CtsDeviceAdmin \
@@ -38,6 +39,7 @@
 	CtsTestStubs \
 	SignatureTest \
 	TestDeviceSetup \
+	CtsUiAutomatorApp \
 	$(cts_security_apps_list)
 
 cts_external_packages := \
@@ -51,6 +53,7 @@
 
 # Test packages that require an associated test package XML.
 cts_test_packages := \
+	$(PTS_TEST_PACKAGES) \
 	CtsAccelerationTestCases \
 	CtsAccountManagerTestCases \
 	CtsAccessibilityServiceTestCases \
@@ -62,8 +65,10 @@
 	CtsCalendarcommon2TestCases \
 	CtsContentTestCases \
 	CtsDatabaseTestCases \
+	CtsDisplayTestCases \
 	CtsDpiTestCases \
 	CtsDpiTestCases2 \
+	CtsDreamsTestCases \
 	CtsDrmTestCases \
 	CtsEffectTestCases \
 	CtsExampleTestCases \
@@ -76,6 +81,7 @@
 	CtsLocationTestCases \
 	CtsMediaStressTestCases \
 	CtsMediaTestCases \
+	CtsNativeOpenGLTestCases \
 	CtsNdefTestCases \
 	CtsNetTestCases \
 	CtsOpenGLTestCases \
@@ -107,6 +113,7 @@
 
 # Host side only tests
 cts_host_libraries := \
+	$(PTS_HOST_CASES) \
 	CtsAppSecurityTests \
 	CtsMonkeyTestCases
 
@@ -115,17 +122,23 @@
 	NativeMediaTest_SL \
 	NativeMediaTest_XA
 
+cts_ui_tests := \
+        CtsUiAutomatorTests
+
 # All the files that will end up under the repository/testcases
 # directory of the final CTS distribution.
 CTS_TEST_CASES := $(call cts-get-lib-paths,$(cts_host_libraries)) \
 		$(call cts-get-package-paths,$(cts_test_packages)) \
-		$(call cts-get-native-paths,$(cts_native_exes))
+		$(call cts-get-native-paths,$(cts_native_exes)) \
+		$(call cts-get-ui-lib-paths,$(cts_ui_tests))
 
 # All the XMLs that will end up under the repository/testcases
 # and that need to be created before making the final CTS distribution.
 CTS_TEST_XMLS := $(call cts-get-test-xmls,$(cts_host_libraries)) \
 		$(call cts-get-test-xmls,$(cts_test_packages)) \
-		$(call cts-get-test-xmls,$(cts_native_exes))
+		$(call cts-get-test-xmls,$(cts_native_exes)) \
+		$(call cts-get-test-xmls,$(cts_ui_tests))
+
 
 # The following files will be placed in the tools directory of the CTS distribution
 CTS_TOOLS_LIST :=
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index aa3f117..c14728e 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -36,7 +36,7 @@
 # Builds and launches CTS Verifier on a device.
 .PHONY: cts-verifier
 cts-verifier: CtsVerifier adb
-	adb install -r $(ANDROID_PRODUCT_OUT)/data/app/CtsVerifier.apk \
+	adb install -r $(PRODUCT_OUT)/data/app/CtsVerifier.apk \
 		&& adb shell "am start -n com.android.cts.verifier/.CtsVerifierActivity"
 
 #
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 6cd196c..bce422d 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -18,7 +18,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
       package="com.android.cts.verifier"
       android:versionCode="1"
-      android:versionName="4.2_r3">
+      android:versionName="1337">
 
     <!-- Using 10 for more complete NFC support... -->
     <uses-sdk android:minSdkVersion="10"></uses-sdk>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/GpsTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/GpsTestActivity.java
index bb12b2b..da1ee88 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/location/GpsTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/location/GpsTestActivity.java
@@ -95,25 +95,25 @@
             case 1:
                 // Test GPS with minTime = 0
                 mLocationVerifier = new LocationVerifier(this, mLocationManager,
-                        LocationManager.GPS_PROVIDER, 0, 8);
+                        LocationManager.GPS_PROVIDER, 0, 8, false);
                 mLocationVerifier.start();
                 break;
             case 2:
                 // Test GPS with minTime = 1s
                 mLocationVerifier = new LocationVerifier(this, mLocationManager,
-                        LocationManager.GPS_PROVIDER, 1 * 1000, 8);
+                        LocationManager.GPS_PROVIDER, 1 * 1000, 8, false);
                 mLocationVerifier.start();
                 break;
             case 3:
                 // Test GPS with minTime = 5s
                 mLocationVerifier = new LocationVerifier(this, mLocationManager,
-                        LocationManager.GPS_PROVIDER, 5 * 1000, 8);
+                        LocationManager.GPS_PROVIDER, 5 * 1000, 8, false);
                 mLocationVerifier.start();
                 break;
             case 4:
                 // Test GPS with minTime = 15s
                 mLocationVerifier = new LocationVerifier(this, mLocationManager,
-                        LocationManager.GPS_PROVIDER, 15 * 1000, 8);
+                        LocationManager.GPS_PROVIDER, 15 * 1000, 8, false);
                 mLocationVerifier.start();
                 break;
             case 5:
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/LocationVerifier.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/LocationVerifier.java
index b6d2a9e..2988db0 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/location/LocationVerifier.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/location/LocationVerifier.java
@@ -62,6 +62,7 @@
     private long mLastPassiveTimestamp = -1;
     private int mNumActiveUpdates = 0;
     private int mNumPassiveUpdates = 0;
+    private boolean mIsMockProvider = false;
     private boolean mRunning = false;
     private boolean mActiveLocationArrive = false;
 
@@ -85,6 +86,11 @@
                 mCb.log("(ignored) active " + mProvider + " update (" + delta + "ms)");
                 return;
             }
+            if (location.isFromMockProvider() != mIsMockProvider) {
+                fail("location coming from \"" + mProvider +
+                        "\" provider reports isFromMockProvider() to be " +
+                        location.isFromMockProvider());
+            }
 
             mActiveDeltas.add(delta);
             mCb.log("active " + mProvider + " update (" + delta + "ms)");
@@ -167,6 +173,11 @@
                 mCb.log("(ignored) passive " + mProvider + " update (" + delta + "ms)");
                 return;
             }
+            if (location.isFromMockProvider() != mIsMockProvider) {
+                fail("location coming from \"" + mProvider +
+                        "\" provider reports isFromMockProvider() to be " +
+                        location.isFromMockProvider());
+            }
 
             mPassiveDeltas.add(delta);
             mCb.log("passive " + mProvider + " update (" + delta + "ms)");
@@ -181,7 +192,7 @@
     }
 
     public LocationVerifier(PassFailLog cb, LocationManager locationManager,
-            String provider, long requestedInterval, int numUpdates) {
+            String provider, long requestedInterval, int numUpdates, boolean isMockProvider) {
         mProvider = provider;
         mInterval = requestedInterval;
         // timeout at 60 seconds after interval time
@@ -192,6 +203,7 @@
         mHandler = new Handler(this);
         mActiveListener = new ActiveListener();
         mPassiveListener = new PassiveListener();
+        mIsMockProvider = isMockProvider;
     }
 
     public void start() {
diff --git a/build/config.mk b/build/config.mk
index c9fa709..b737ef7 100644
--- a/build/config.mk
+++ b/build/config.mk
@@ -14,4 +14,6 @@
 
 BUILD_CTS_EXECUTABLE := cts/build/test_executable.mk
 BUILD_CTS_PACKAGE := cts/build/test_package.mk
+BUILD_CTS_GTEST_PACKAGE := cts/build/test_gtest_package.mk
 BUILD_CTS_HOST_JAVA_LIBRARY := cts/build/test_host_java_library.mk
+BUILD_CTS_UI_JAVA_LIBRARY := cts/build/test_uiautomator.mk
diff --git a/build/test_gtest_package.mk b/build/test_gtest_package.mk
new file mode 100644
index 0000000..2f4c9bb
--- /dev/null
+++ b/build/test_gtest_package.mk
@@ -0,0 +1,49 @@
+# Copyright 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Builds a package and defines a rule to generate the associated test
+# package XML needed by CTS.
+#
+# Replace "include $(BUILD_PACKAGE)" with "include $(BUILD_CTS_GTEST_PACKAGE)"
+#
+
+# Disable by default so "m cts" will work in emulator builds
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
+
+cts_package_apk := $(CTS_TESTCASES_OUT)/$(LOCAL_PACKAGE_NAME).apk
+cts_package_xml := $(CTS_TESTCASES_OUT)/$(LOCAL_PACKAGE_NAME).xml
+
+$(cts_package_apk): PRIVATE_PACKAGE := $(LOCAL_PACKAGE_NAME)
+$(cts_package_apk): $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME))/package.apk | $(ACP)
+	$(hide) mkdir -p $(CTS_TESTCASES_OUT)
+	$(hide) $(ACP) -fp $(call intermediates-dir-for,APPS,$(PRIVATE_PACKAGE))/package.apk $@
+
+$(cts_package_xml): PRIVATE_PATH := $(LOCAL_PATH)
+$(cts_package_xml): PRIVATE_TEST_PACKAGE := android.$(notdir $(LOCAL_PATH))
+$(cts_package_xml): PRIVATE_EXECUTABLE := $(LOCAL_MODULE)
+$(cts_package_xml): PRIVATE_MANIFEST := $(LOCAL_PATH)/AndroidManifest.xml
+$(cts_package_xml): $(addprefix $(LOCAL_PATH)/,$(LOCAL_SRC_FILES))  $(CTS_NATIVE_TEST_SCANNER) $(CTS_XML_GENERATOR)
+	$(hide) echo Generating test description for wrapped native package $(PRIVATE_EXECUTABLE)
+	$(hide) mkdir -p $(CTS_TESTCASES_OUT)
+	$(hide) $(CTS_NATIVE_TEST_SCANNER) -s $(PRIVATE_PATH) \
+						-t $(PRIVATE_TEST_PACKAGE) | \
+			$(CTS_XML_GENERATOR) -t wrappednative \
+                                                -m $(PRIVATE_MANIFEST) \
+						-n $(PRIVATE_EXECUTABLE) \
+						-p $(PRIVATE_TEST_PACKAGE) \
+						-e $(CTS_EXPECTATIONS) \
+						-o $@
diff --git a/build/test_package.mk b/build/test_package.mk
index 071acee..aa9a4dd 100644
--- a/build/test_package.mk
+++ b/build/test_package.mk
@@ -35,7 +35,12 @@
 $(cts_package_xml): PRIVATE_PATH := $(LOCAL_PATH)
 $(cts_package_xml): PRIVATE_INSTRUMENTATION := $(LOCAL_INSTRUMENTATION_FOR)
 $(cts_package_xml): PRIVATE_PACKAGE := $(LOCAL_PACKAGE_NAME)
-$(cts_package_xml): PRIVATE_TEST_PACKAGE := android.$(notdir $(LOCAL_PATH))
+ifneq ($(filter cts/suite/pts/%, $(LOCAL_PATH)),) # PTS
+PRIVATE_CTS_TEST_PACKAGE_NANE_ := com.android.pts.$(notdir $(LOCAL_PATH))
+else # CTS
+PRIVATE_CTS_TEST_PACKAGE_NANE_ := android.$(notdir $(LOCAL_PATH))
+endif # PTS
+$(cts_package_xml): PRIVATE_TEST_PACKAGE := $(PRIVATE_CTS_TEST_PACKAGE_NANE_)
 $(cts_package_xml): PRIVATE_MANIFEST := $(LOCAL_PATH)/AndroidManifest.xml
 $(cts_package_xml): PRIVATE_TEST_TYPE := $(if $(LOCAL_CTS_TEST_RUNNER),$(LOCAL_CTS_TEST_RUNNER),'')
 $(cts_package_xml): $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME))/package.apk $(CTS_EXPECTATIONS) $(CTS_JAVA_TEST_SCANNER_DOCLET) $(CTS_JAVA_TEST_SCANNER) $(CTS_XML_GENERATOR)
diff --git a/build/test_uiautomator.mk b/build/test_uiautomator.mk
new file mode 100644
index 0000000..255f70b
--- /dev/null
+++ b/build/test_uiautomator.mk
@@ -0,0 +1,49 @@
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Builds a uiautomator library and defines a rule to generate the associated test
+# package XML needed by CTS.
+#
+
+include $(BUILD_JAVA_LIBRARY)
+
+cts_library_xml := $(CTS_TESTCASES_OUT)/$(LOCAL_MODULE).xml 
+cts_library_jar := $(CTS_TESTCASES_OUT)/$(LOCAL_MODULE).jar
+
+$(cts_library_jar): PRIVATE_MODULE := $(LOCAL_MODULE)
+$(cts_library_jar): $(call intermediates-dir-for,JAVA_LIBRARIES,$(LOCAL_MODULE))/javalib.jar | $(ACP)
+	$(hide) mkdir -p $(CTS_TESTCASES_OUT)
+	$(hide) $(ACP) -fp $(call intermediates-dir-for,JAVA_LIBRARIES,$(PRIVATE_MODULE))/javalib.jar $@
+
+$(cts_library_xml): PRIVATE_PATH := $(LOCAL_PATH)/src
+$(cts_library_xml): PRIVATE_TEST_APP_PACKAGE := $(LOCAL_CTS_TEST_APP_PACKAGE)
+$(cts_library_xml): PRIVATE_TEST_PACKAGE := $(LOCAL_CTS_TEST_PACKAGE)
+$(cts_library_xml): PRIVATE_TEST_APK := $(LOCAL_CTS_TEST_APK)
+$(cts_library_xml): PRIVATE_LIBRARY := $(LOCAL_MODULE)
+$(cts_library_xml): PRIVATE_JAR_PATH := $(LOCAL_MODULE).jar
+$(cts_library_xml): $(call intermediates-dir-for,JAVA_LIBRARIES,$(LOCAL_MODULE))/javalib.jar $(CTS_EXPECTATIONS) $(CTS_JAVA_TEST_SCANNER_DOCLET) $(CTS_JAVA_TEST_SCANNER) $(CTS_XML_GENERATOR)
+	$(hide) echo Generating test description for uiautomator library $(PRIVATE_LIBRARY)
+	$(hide) mkdir -p $(CTS_TESTCASES_OUT)
+	$(hide) $(CTS_JAVA_TEST_SCANNER) -s $(PRIVATE_PATH) \
+						-d $(CTS_JAVA_TEST_SCANNER_DOCLET) | \
+			$(CTS_XML_GENERATOR) -t uiAutomator \
+						-i $(PRIVATE_TEST_APK) \
+						-j $(PRIVATE_JAR_PATH) \
+						-a $(PRIVATE_TEST_PACKAGE) \
+						-n $(PRIVATE_LIBRARY) \
+						-p $(PRIVATE_TEST_PACKAGE) \
+						-r $(PRIVATE_TEST_APP_PACKAGE) \
+						-e $(CTS_EXPECTATIONS) \
+						-o $@
diff --git a/libs/testserver/src/android/webkit/cts/CtsTestServer.java b/libs/testserver/src/android/webkit/cts/CtsTestServer.java
index 16d57ea..a18d329 100755
--- a/libs/testserver/src/android/webkit/cts/CtsTestServer.java
+++ b/libs/testserver/src/android/webkit/cts/CtsTestServer.java
@@ -423,11 +423,20 @@
     }
 
     /**
+     * Hook for adding stuffs for HTTP POST. Default implementation does nothing.
+     * @return null to use the default response mechanism of sending the requested uri as it is.
+     *         Otherwise, the whole response should be handled inside onPost.
+     */
+    protected HttpResponse onPost(HttpRequest request) throws Exception {
+        return null;
+    }
+
+    /**
      * Generate a response to the given request.
      * @throws InterruptedException
      * @throws IOException
      */
-    private HttpResponse getResponse(HttpRequest request) throws InterruptedException, IOException {
+    private HttpResponse getResponse(HttpRequest request) throws Exception {
         RequestLine requestLine = request.getRequestLine();
         HttpResponse response = null;
         String uriString = requestLine.getUri();
@@ -441,6 +450,13 @@
             }
         }
 
+        if (requestLine.getMethod().equals("POST")) {
+            HttpResponse responseOnPost = onPost(request);
+            if (responseOnPost != null) {
+                return responseOnPost;
+            }
+        }
+
         URI uri = URI.create(uriString);
         String path = uri.getPath();
         String query = uri.getQuery();
@@ -544,12 +560,11 @@
             Header[] cookies = request.getHeaders("Cookie");
             Pattern p = Pattern.compile("count=(\\d+)");
             StringBuilder cookieString = new StringBuilder(100);
+            cookieString.append(cookies.length);
             int count = 0;
             for (Header cookie : cookies) {
+                cookieString.append("|");
                 String value = cookie.getValue();
-                if (cookieString.length() > 0) {
-                    cookieString.append("|");
-                }
                 cookieString.append(value);
                 Matcher m = p.matcher(value);
                 if (m.find()) {
@@ -864,7 +879,7 @@
             }
 
             @Override
-            public Void call() throws IOException, InterruptedException, HttpException {
+            public Void call() throws Exception {
                 HttpResponse response = mServer.getResponse(mRequest);
                 mConnection.sendResponseHeader(response);
                 mConnection.sendResponseEntity(response);
diff --git a/tests/tests/openglperf/src/android/openglperf/cts/RenderingWatchDog.java b/libs/util/src/android/cts/util/WatchDog.java
similarity index 91%
rename from tests/tests/openglperf/src/android/openglperf/cts/RenderingWatchDog.java
rename to libs/util/src/android/cts/util/WatchDog.java
index 4872af2..3db90e9 100644
--- a/tests/tests/openglperf/src/android/openglperf/cts/RenderingWatchDog.java
+++ b/libs/util/src/android/cts/util/WatchDog.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.openglperf.cts;
+package android.cts.util;
 
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
@@ -26,14 +26,14 @@
  * class for checking if rendering function is alive or not.
  * panic if watch-dog is not reset over certain amount of time
  */
-public class RenderingWatchDog implements Runnable {
-    private static final String TAG = "RenderingWatchDog";
+public class WatchDog implements Runnable {
+    private static final String TAG = "WatchDog";
     private Thread mThread;
     private Semaphore mSemaphore;
     private volatile boolean mStopRequested;
     private final long mTimeoutInMilliSecs;
 
-    public RenderingWatchDog(long timeoutInMilliSecs) {
+    public WatchDog(long timeoutInMilliSecs) {
         mTimeoutInMilliSecs = timeoutInMilliSecs;
     }
 
diff --git a/suite/pts/tools/tradefed/Android.mk b/libs/wrappedgtest/Android.mk
similarity index 74%
rename from suite/pts/tools/tradefed/Android.mk
rename to libs/wrappedgtest/Android.mk
index 09e49b1..f89ba9d 100644
--- a/suite/pts/tools/tradefed/Android.mk
+++ b/libs/wrappedgtest/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2011 The Android Open Source Project
+# Copyright 2012 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -12,11 +12,14 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-LOCAL_PATH := $(call my-dir)
+LOCAL_PATH:= $(call my-dir)
+
 include $(CLEAR_VARS)
 
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_PREBUILT_EXECUTABLES := pts-tradefed
-include $(BUILD_HOST_PREBUILT)
+LOCAL_MODULE := ctswrappedgtest
 
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/libs/wrappedgtest/src/WrappedGTestActivity.java b/libs/wrappedgtest/src/WrappedGTestActivity.java
new file mode 100644
index 0000000..0633a5b
--- /dev/null
+++ b/libs/wrappedgtest/src/WrappedGTestActivity.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.test.wrappedgtest;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.os.Bundle;
+
+public class WrappedGTestActivity extends Activity {
+
+    private WrappedGTestInstrumentation mInstrumentation;
+
+    public void setInstrumentation(WrappedGTestInstrumentation instrumentation) {
+        mInstrumentation = instrumentation;
+    }
+
+    public int runGTests() {
+        return runTests(this);
+    }
+
+    public void sendStatus(String output) {
+        Bundle outputBundle = new Bundle();
+        outputBundle.putString("gtest", output);
+        mInstrumentation.sendStatus(1, outputBundle);
+    }
+
+    protected static native int runTests(WrappedGTestActivity activity);
+}
diff --git a/libs/wrappedgtest/src/WrappedGTestInstrumentation.java b/libs/wrappedgtest/src/WrappedGTestInstrumentation.java
new file mode 100644
index 0000000..b29aaab
--- /dev/null
+++ b/libs/wrappedgtest/src/WrappedGTestInstrumentation.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.test.wrappedgtest;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.app.KeyguardManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+
+public class WrappedGTestInstrumentation extends Instrumentation {
+
+    private static final String TAG = "WrappedGTestInstrumentation";
+    private WrappedGTestActivity mActivity;
+    protected Class mActivityClass;
+
+    public WrappedGTestInstrumentation() {
+    }
+
+    @Override
+    public void onCreate(Bundle arguments) {
+        // attempt to disable keyguard,  if current test has permission to do so
+        if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD)
+                == PackageManager.PERMISSION_GRANTED) {
+            Log.i(TAG, "Disabling keyguard");
+            KeyguardManager keyguardManager =
+                (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
+            keyguardManager.newKeyguardLock("cts").disableKeyguard();
+        } else {
+            Log.i(TAG, "Test lacks permission to disable keyguard. " +
+                    "UI based tests may fail if keyguard is up");
+        }
+        super.onCreate(arguments);
+        start();
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+
+        Intent intent = new Intent(getTargetContext(), mActivityClass);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        mActivity = (WrappedGTestActivity)startActivitySync(intent);
+        mActivity.setInstrumentation(this);
+        mActivity.runGTests();
+
+        finish(Activity.RESULT_OK, new Bundle());
+    }
+}
diff --git a/suite/pts/Android.mk b/suite/pts/Android.mk
index 07b30e3..c141484 100644
--- a/suite/pts/Android.mk
+++ b/suite/pts/Android.mk
@@ -14,5 +14,4 @@
 # limitations under the License.
 #
 
-include cts/suite/pts/PtsBuild.mk
 include $(call all-subdir-makefiles)
diff --git a/suite/pts/PtsBenchmarkingList.mk b/suite/pts/PtsBenchmarkingList.mk
new file mode 100644
index 0000000..7c0ad47
--- /dev/null
+++ b/suite/pts/PtsBenchmarkingList.mk
@@ -0,0 +1,36 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# package definitions for PTS
+
+# New packages should be added here
+PTS_TEST_PACKAGES := \
+    PtsDeviceFilePerf \
+    PtsDeviceUi \
+    PtsDeviceDram \
+    PtsDeviceSimpleCpu \
+    PtsDeviceBrowserBench \
+    PtsDeviceVideoPerf \
+    PtsDeviceOpenGl
+
+PTS_SUPPORT_PACKAGES := \
+    PtsDeviceTaskswitchingAppA \
+    PtsDeviceTaskswitchingAppB \
+    PtsDeviceTaskswitchingControl
+
+PTS_HOST_CASES := \
+    PtsHostBootup \
+    PtsHostUi
diff --git a/suite/pts/PtsBuild.mk b/suite/pts/PtsBuild.mk
deleted file mode 100644
index 18306b9..0000000
--- a/suite/pts/PtsBuild.mk
+++ /dev/null
@@ -1,152 +0,0 @@
-#
-# Copyright (C) 2012 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-# several makefiles for CTS merged for PTS
-
-LOCAL_PATH:= $(call my-dir)
-
-# New packages should be added here
-PTS_TEST_PACKAGES := \
-    PtsDeviceFilePerf \
-    PtsDeviceUi \
-    PtsDeviceDram
-
-
-PTS_SUPPORT_PACKAGES := \
-    TestDeviceSetup \
-    PtsDeviceTaskswitchingAppA \
-    PtsDeviceTaskswitchingAppB \
-    PtsDeviceTaskswitchingControl \
-    com.replica.replicaisland \
-    PtsDeviceBrowserLauncher
-
-PTS_HOST_CASES := \
-    PtsHostBootup \
-    PtsHostUi \
-    PtsHostBrowser
-
-PTS_HOST_LIBS := \
-    $(HOST_OUT_JAVA_LIBRARIES)/ptscommonutilhost.jar \
-    $(HOST_OUT_JAVA_LIBRARIES)/ptshostutil.jar
-
-BUILD_PTS_PACKAGE := cts/suite/pts/build/test_package.mk
-BUILD_PTS_HOST_JAVA_LIBRARY := cts/suite/pts/build/test_host_java_library.mk
-
-PTS_JAVA_TEST_SCANNER := $(HOST_OUT_EXECUTABLES)/cts-java-scanner
-PTS_JAVA_TEST_SCANNER_DOCLET := $(HOST_OUT_JAVA_LIBRARIES)/cts-java-scanner-doclet.jar
-
-# Generator of test XMLs from scanner output.
-PTS_XML_GENERATOR := $(HOST_OUT_EXECUTABLES)/cts-xml-generator
-
-# File indicating which tests should be blacklisted due to problems.
-PTS_EXPECTATIONS := cts/suite/pts/expectations/knownfailures.txt
-
-PTS_TESTCASES_OUT := $(HOST_OUT)/pts-testcases
-
-define pts-get-package-paths
-	$(foreach pkg,$(1),$(PTS_TESTCASES_OUT)/$(pkg).apk)
-endef
-
-define pts-get-test-xmls
-	$(foreach name,$(1),$(PTS_TESTCASES_OUT)/$(name).xml)
-endef
-
-define pts-get-lib-paths
-	$(foreach lib,$(1),$(HOST_OUT_JAVA_LIBRARIES)/$(lib).jar)
-endef
-
-PTS_TEST_CASE_LIST := \
-	$(PTS_SUPPORT_PACKAGES)
-
-PTS_TEST_CASES := \
-		$(call pts-get-package-paths,$(PTS_TEST_PACKAGES)) \
-		$(call pts-get-lib-paths,$(PTS_HOST_CASES))
-
-PTS_TEST_XMLS := \
-    $(call pts-get-test-xmls,$(PTS_TEST_PACKAGES)) \
-    $(call pts-get-test-xmls,$(PTS_HOST_CASES))
-
-pts_dir := $(HOST_OUT)/pts
-pts_tools_src_dir := cts/tools
-
-pts_name := android-pts
-
-DDMLIB_JAR := $(HOST_OUT_JAVA_LIBRARIES)/ddmlib-prebuilt.jar
-junit_host_jar := $(HOST_OUT_JAVA_LIBRARIES)/junit.jar
-HOSTTESTLIB_JAR := $(HOST_OUT_JAVA_LIBRARIES)/hosttestlib.jar
-TF_JAR := $(HOST_OUT_JAVA_LIBRARIES)/tradefed-prebuilt.jar
-PTS_TF_JAR := $(HOST_OUT_JAVA_LIBRARIES)/cts-tradefed.jar
-PTS_TF_EXEC := $(HOST_OUT_EXECUTABLES)/pts-tradefed
-PTS_TF_README := $(pts_tools_src_dir)/tradefed-host/README
-
-
-DEFAULT_TEST_PLAN := $(pts_dir)/$(pts_name)/resource/plans/PTS.xml
-
-$(pts_dir)/all_pts_files_stamp: PRIVATE_JUNIT_HOST_JAR := $(junit_host_jar)
-
-$(pts_dir)/all_pts_files_stamp: $(PTS_TEST_CASES) $(PTS_TEST_CASE_LIST) $(junit_host_jar) $(HOSTTESTLIB_JAR) $(PTS_HOST_LIBRARY_JARS) $(TF_JAR) $(VMTESTSTF_JAR) $(PTS_TF_JAR) $(PTS_TF_EXEC) $(PTS_TF_README) $(ACP) $(PTS_HOST_LIBS)
-# Make necessary directory for PTS
-	$(hide) rm -rf $(PRIVATE_PTS_DIR)
-	$(hide) mkdir -p $(TMP_DIR)
-	$(hide) mkdir -p $(PRIVATE_DIR)/docs
-	$(hide) mkdir -p $(PRIVATE_DIR)/tools
-	$(hide) mkdir -p $(PRIVATE_DIR)/repository/testcases
-	$(hide) mkdir -p $(PRIVATE_DIR)/repository/plans
-# Copy executable and JARs to PTS directory
-	$(hide) $(ACP) -fp $(DDMLIB_JAR) $(PRIVATE_JUNIT_HOST_JAR) $(HOSTTESTLIB_JAR) $(PTS_HOST_LIBRARY_JARS) $(TF_JAR) $(PTS_TF_JAR) $(PTS_TF_EXEC) $(PTS_TF_README) $(PTS_HOST_LIBS) $(PRIVATE_DIR)/tools
-# Change mode of the executables
-	$(foreach apk,$(PTS_TEST_CASE_LIST),$(call copy-testcase-apk,$(apk)))
-	$(foreach testcase,$(PTS_TEST_CASES),$(call copy-testcase,$(testcase)))
-	$(hide) touch $@
-
-# Generate the default test plan for User.
-# Usage: buildCts.py <testRoot> <ctsOutputDir> <tempDir> <androidRootDir> <docletPath>
-
-$(DEFAULT_TEST_PLAN): $(pts_dir)/all_pts_files_stamp $(pts_tools_src_dir)/utils/buildCts.py $(HOST_OUT_JAVA_LIBRARIES)/descGen.jar $(PTS_TEST_XMLS) | $(ACP)
-	$(hide) $(ACP) -fp $(PTS_TEST_XMLS) $(PRIVATE_DIR)/repository/testcases
-	$(hide) $(pts_tools_src_dir)/utils/buildCts.py cts/suite/pts $(PRIVATE_DIR) $(TMP_DIR) \
-		$(TOP) $(HOST_OUT_JAVA_LIBRARIES)/descGen.jar -pts
-
-# Package PTS and clean up.
-INTERNAL_PTS_TARGET := $(pts_dir)/$(pts_name).zip
-$(INTERNAL_PTS_TARGET): PRIVATE_NAME := $(pts_name)
-$(INTERNAL_PTS_TARGET): PRIVATE_PTS_DIR := $(pts_dir)
-$(INTERNAL_PTS_TARGET): PRIVATE_DIR := $(pts_dir)/$(pts_name)
-$(INTERNAL_PTS_TARGET): TMP_DIR := $(pts_dir)/temp
-$(INTERNAL_PTS_TARGET): $(pts_dir)/all_pts_files_stamp $(DEFAULT_TEST_PLAN)
-	$(hide) echo "Package PTS: $@"
-	$(hide) cd $(dir $@) && zip -rq $(notdir $@) $(PRIVATE_NAME)
-
-.PHONY: pts
-pts: $(INTERNAL_PTS_TARGET)
-cts: pts
-# generate PTS during CTS build
-ifneq ($(filter cts, $(MAKECMDGOALS)),)
-$(call dist-for-goals,cts,$(INTERNAL_PTS_TARGET))
-endif
-
-define copy-testcase-apk
-
-$(hide) $(ACP) -fp $(call intermediates-dir-for,APPS,$(1))/package.apk \
-	$(PRIVATE_DIR)/repository/testcases/$(1).apk
-
-endef
-
-define copy-testcase
-
-$(hide) $(ACP) -fp $(1) $(PRIVATE_DIR)/repository/testcases/$(notdir $1)
-
-endef
diff --git a/suite/pts/build/test_host_java_library.mk b/suite/pts/build/test_host_java_library.mk
deleted file mode 100644
index 3f410d1..0000000
--- a/suite/pts/build/test_host_java_library.mk
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright (C) 2012 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-#
-# Builds a host library and defines a rule to generate the associated test
-# package XML needed by PTS.
-#
-
-include $(BUILD_HOST_JAVA_LIBRARY)
-
-pts_library_xml := $(PTS_TESTCASES_OUT)/$(LOCAL_MODULE).xml
-
-$(pts_library_xml): PRIVATE_PATH := $(LOCAL_PATH)/src
-$(pts_library_xml): PRIVATE_TEST_PACKAGE := $(LOCAL_PTS_TEST_PACKAGE)
-$(pts_library_xml): PRIVATE_LIBRARY := $(LOCAL_MODULE)
-$(pts_library_xml): PRIVATE_JAR_PATH := $(LOCAL_MODULE).jar
-$(pts_library_xml): $(HOST_OUT_JAVA_LIBRARIES)/$(LOCAL_MODULE).jar $(PTS_EXPECTATIONS) $(PTS_JAVA_TEST_SCANNER_DOCLET) $(PTS_JAVA_TEST_SCANNER) $(PTS_XML_GENERATOR)
-	$(hide) echo Generating test description for host library $(PRIVATE_LIBRARY)
-	$(hide) mkdir -p $(PTS_TESTCASES_OUT)
-	$(hide) $(PTS_JAVA_TEST_SCANNER) -s $(PRIVATE_PATH) \
-						-d $(PTS_JAVA_TEST_SCANNER_DOCLET) | \
-			$(PTS_XML_GENERATOR) -t hostSideOnly \
-						-j $(PRIVATE_JAR_PATH) \
-						-n $(PRIVATE_LIBRARY) \
-						-p $(PRIVATE_TEST_PACKAGE) \
-						-e $(PTS_EXPECTATIONS) \
-						-o $@
diff --git a/suite/pts/build/test_package.mk b/suite/pts/build/test_package.mk
deleted file mode 100644
index f677c3b..0000000
--- a/suite/pts/build/test_package.mk
+++ /dev/null
@@ -1,49 +0,0 @@
-# Copyright (C) 2012 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# copied from cts/build. modified for PTS
-
-# Disable by default so "m pts" will work in emulator builds
-LOCAL_DEX_PREOPT := false
-
-include $(BUILD_PACKAGE)
-
-pts_package_apk := $(PTS_TESTCASES_OUT)/$(LOCAL_PACKAGE_NAME).apk
-pts_package_xml := $(PTS_TESTCASES_OUT)/$(LOCAL_PACKAGE_NAME).xml
-
-$(pts_package_apk): PRIVATE_PACKAGE := $(LOCAL_PACKAGE_NAME)
-$(pts_package_apk): $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME))/package.apk | $(ACP)
-	$(hide) mkdir -p $(PTS_TESTCASES_OUT)
-	$(hide) $(ACP) -fp $< $@
-
-$(pts_package_xml): PRIVATE_PATH := $(LOCAL_PATH)
-$(pts_package_xml): PRIVATE_INSTRUMENTATION := $(LOCAL_INSTRUMENTATION_FOR)
-$(pts_package_xml): PRIVATE_PACKAGE := $(LOCAL_PACKAGE_NAME)
-$(pts_package_xml): PRIVATE_TEST_PACKAGE := com.android.pts.$(notdir $(LOCAL_PATH))
-$(pts_package_xml): PRIVATE_MANIFEST := $(LOCAL_PATH)/AndroidManifest.xml
-$(pts_package_xml): PRIVATE_TEST_TYPE := $(if $(LOCAL_PTS_TEST_RUNNER),$(LOCAL_PTS_TEST_RUNNER),'')
-$(pts_package_xml): $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME))/package.apk $(PTS_EXPECTATIONS) $(PTS_JAVA_TEST_SCANNER_DOCLET) $(PTS_JAVA_TEST_SCANNER) $(PTS_XML_GENERATOR)
-	$(hide) echo Generating test description for java package $(PRIVATE_PACKAGE)
-	$(hide) mkdir -p $(PTS_TESTCASES_OUT)
-	$(hide) $(PTS_JAVA_TEST_SCANNER) \
-						-s $(PRIVATE_PATH) \
-						-d $(PTS_JAVA_TEST_SCANNER_DOCLET) | \
-			$(PTS_XML_GENERATOR) \
-						-t $(PRIVATE_TEST_TYPE) \
-						-m $(PRIVATE_MANIFEST) \
-						-i "$(PRIVATE_INSTRUMENTATION)" \
-						-n $(PRIVATE_PACKAGE) \
-						-p $(PRIVATE_TEST_PACKAGE) \
-						-e $(PTS_EXPECTATIONS) \
-						-o $@
diff --git a/suite/pts/hostTests/browser/browserlauncher/Android.mk b/suite/pts/deviceTests/browserbench/Android.mk
similarity index 92%
rename from suite/pts/hostTests/browser/browserlauncher/Android.mk
rename to suite/pts/deviceTests/browserbench/Android.mk
index 0592017..1ce0e49 100644
--- a/suite/pts/hostTests/browser/browserlauncher/Android.mk
+++ b/suite/pts/deviceTests/browserbench/Android.mk
@@ -24,10 +24,10 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_PACKAGE_NAME := PtsDeviceBrowserLauncher
+LOCAL_PACKAGE_NAME := PtsDeviceBrowserBench
 
 LOCAL_SDK_VERSION := 16
 
-include $(BUILD_PTS_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/suite/pts/hostTests/browser/browserlauncher/AndroidManifest.xml b/suite/pts/deviceTests/browserbench/AndroidManifest.xml
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/AndroidManifest.xml
rename to suite/pts/deviceTests/browserbench/AndroidManifest.xml
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/base.js b/suite/pts/deviceTests/browserbench/assets/octane/base.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/base.js
rename to suite/pts/deviceTests/browserbench/assets/octane/base.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/box2d.js b/suite/pts/deviceTests/browserbench/assets/octane/box2d.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/box2d.js
rename to suite/pts/deviceTests/browserbench/assets/octane/box2d.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/code-load.js b/suite/pts/deviceTests/browserbench/assets/octane/code-load.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/code-load.js
rename to suite/pts/deviceTests/browserbench/assets/octane/code-load.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/crypto.js b/suite/pts/deviceTests/browserbench/assets/octane/crypto.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/crypto.js
rename to suite/pts/deviceTests/browserbench/assets/octane/crypto.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/css/bootstrap-responsive.css b/suite/pts/deviceTests/browserbench/assets/octane/css/bootstrap-responsive.css
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/css/bootstrap-responsive.css
rename to suite/pts/deviceTests/browserbench/assets/octane/css/bootstrap-responsive.css
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/css/bootstrap.css b/suite/pts/deviceTests/browserbench/assets/octane/css/bootstrap.css
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/css/bootstrap.css
rename to suite/pts/deviceTests/browserbench/assets/octane/css/bootstrap.css
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/css/docs.css b/suite/pts/deviceTests/browserbench/assets/octane/css/docs.css
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/css/docs.css
rename to suite/pts/deviceTests/browserbench/assets/octane/css/docs.css
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/deltablue.js b/suite/pts/deviceTests/browserbench/assets/octane/deltablue.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/deltablue.js
rename to suite/pts/deviceTests/browserbench/assets/octane/deltablue.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/earley-boyer.js b/suite/pts/deviceTests/browserbench/assets/octane/earley-boyer.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/earley-boyer.js
rename to suite/pts/deviceTests/browserbench/assets/octane/earley-boyer.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/gbemu.js b/suite/pts/deviceTests/browserbench/assets/octane/gbemu.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/gbemu.js
rename to suite/pts/deviceTests/browserbench/assets/octane/gbemu.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/ico/apple-touch-icon-114-precomposed.png b/suite/pts/deviceTests/browserbench/assets/octane/ico/apple-touch-icon-114-precomposed.png
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/ico/apple-touch-icon-114-precomposed.png
rename to suite/pts/deviceTests/browserbench/assets/octane/ico/apple-touch-icon-114-precomposed.png
Binary files differ
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/ico/apple-touch-icon-144-precomposed.png b/suite/pts/deviceTests/browserbench/assets/octane/ico/apple-touch-icon-144-precomposed.png
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/ico/apple-touch-icon-144-precomposed.png
rename to suite/pts/deviceTests/browserbench/assets/octane/ico/apple-touch-icon-144-precomposed.png
Binary files differ
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/ico/apple-touch-icon-57-precomposed.png b/suite/pts/deviceTests/browserbench/assets/octane/ico/apple-touch-icon-57-precomposed.png
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/ico/apple-touch-icon-57-precomposed.png
rename to suite/pts/deviceTests/browserbench/assets/octane/ico/apple-touch-icon-57-precomposed.png
Binary files differ
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/ico/apple-touch-icon-72-precomposed.png b/suite/pts/deviceTests/browserbench/assets/octane/ico/apple-touch-icon-72-precomposed.png
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/ico/apple-touch-icon-72-precomposed.png
rename to suite/pts/deviceTests/browserbench/assets/octane/ico/apple-touch-icon-72-precomposed.png
Binary files differ
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/ico/favicon.ico b/suite/pts/deviceTests/browserbench/assets/octane/ico/favicon.ico
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/ico/favicon.ico
rename to suite/pts/deviceTests/browserbench/assets/octane/ico/favicon.ico
Binary files differ
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/index.html b/suite/pts/deviceTests/browserbench/assets/octane/index.html
similarity index 98%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/index.html
rename to suite/pts/deviceTests/browserbench/assets/octane/index.html
index 0e2a79d..704123f 100644
--- a/suite/pts/hostTests/browser/browserlauncher/assets/octane/index.html
+++ b/suite/pts/deviceTests/browserbench/assets/octane/index.html
@@ -15,6 +15,8 @@
 <script src="js/bootstrap-transition.js"></script>
 <script src="js/bootstrap-collapse.js"></script>
 <!-- Octane benchmark code -->
+<!-- PTS -->
+<script type="text/javascript" src="pts_report.js"></script>
 <script type="text/javascript" src="base.js"></script>
 <script type="text/javascript" src="richards.js"></script>
 <script type="text/javascript" src="deltablue.js"></script>
@@ -43,7 +45,8 @@
   }
 
   function AddResult(name, result) {
-    console.log(name + ': ' + result);
+    //PTS
+    PtsReport(name, result, false);
     var box = document.getElementById("Result-" + name);
     box.innerHTML = result;
   }
@@ -65,8 +68,8 @@
     } else {
       status.innerHTML = "Octane Score (incomplete): " + score;
     }
-    // print the result for the host
-    console.log(status.innerHTML);
+    //PTS
+    PtsReport("Octane Score" + (success ? " " : "(incomplete)"), score, true);
     document.getElementById("progress-bar-container").style.visibility = 'hidden';
     document.getElementById("bottom-text").style.visibility = 'visible';
     document.getElementById("inside-anchor").removeChild(document.getElementById("bar-appendix"));
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/js/bootstrap-collapse.js b/suite/pts/deviceTests/browserbench/assets/octane/js/bootstrap-collapse.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/js/bootstrap-collapse.js
rename to suite/pts/deviceTests/browserbench/assets/octane/js/bootstrap-collapse.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/js/bootstrap-transition.js b/suite/pts/deviceTests/browserbench/assets/octane/js/bootstrap-transition.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/js/bootstrap-transition.js
rename to suite/pts/deviceTests/browserbench/assets/octane/js/bootstrap-transition.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/js/jquery.js b/suite/pts/deviceTests/browserbench/assets/octane/js/jquery.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/js/jquery.js
rename to suite/pts/deviceTests/browserbench/assets/octane/js/jquery.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/mandreel.js b/suite/pts/deviceTests/browserbench/assets/octane/mandreel.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/mandreel.js
rename to suite/pts/deviceTests/browserbench/assets/octane/mandreel.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/navier-stokes.js b/suite/pts/deviceTests/browserbench/assets/octane/navier-stokes.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/navier-stokes.js
rename to suite/pts/deviceTests/browserbench/assets/octane/navier-stokes.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/pdfjs.js b/suite/pts/deviceTests/browserbench/assets/octane/pdfjs.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/pdfjs.js
rename to suite/pts/deviceTests/browserbench/assets/octane/pdfjs.js
diff --git a/suite/pts/deviceTests/browserbench/assets/octane/pts_report.html b/suite/pts/deviceTests/browserbench/assets/octane/pts_report.html
new file mode 100644
index 0000000..40e2de6
--- /dev/null
+++ b/suite/pts/deviceTests/browserbench/assets/octane/pts_report.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<html>
+  <head>
+    <title>pts_report</title>
+  </head>
+  <body>
+  </body>
+</html>
diff --git a/suite/pts/deviceTests/browserbench/assets/octane/pts_report.js b/suite/pts/deviceTests/browserbench/assets/octane/pts_report.js
new file mode 100644
index 0000000..fb320ae
--- /dev/null
+++ b/suite/pts/deviceTests/browserbench/assets/octane/pts_report.js
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+/**
+ *  Utility to report benchmarking result via HTTP POST
+ *  to PTS.
+ * @param msg message to add to the report
+ * @param score resulting score
+ * @param isFinal true if this is the last / final score
+ */
+function PtsReport(msg, score, isFinal)
+{
+    req = new XMLHttpRequest();
+    req.open("POST", "pts_report.html?final=" + (isFinal ? "1" : "0") +
+             "&score=" + score + "&message=" + msg, false);
+    req.send(null)
+}
+
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/raytrace.js b/suite/pts/deviceTests/browserbench/assets/octane/raytrace.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/raytrace.js
rename to suite/pts/deviceTests/browserbench/assets/octane/raytrace.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/regexp.js b/suite/pts/deviceTests/browserbench/assets/octane/regexp.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/regexp.js
rename to suite/pts/deviceTests/browserbench/assets/octane/regexp.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/richards.js b/suite/pts/deviceTests/browserbench/assets/octane/richards.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/richards.js
rename to suite/pts/deviceTests/browserbench/assets/octane/richards.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/run.js b/suite/pts/deviceTests/browserbench/assets/octane/run.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/run.js
rename to suite/pts/deviceTests/browserbench/assets/octane/run.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/splay.js b/suite/pts/deviceTests/browserbench/assets/octane/splay.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/splay.js
rename to suite/pts/deviceTests/browserbench/assets/octane/splay.js
diff --git a/suite/pts/deviceTests/browserbench/src/com/android/pts/browser/BrowserBenchTest.java b/suite/pts/deviceTests/browserbench/src/com/android/pts/browser/BrowserBenchTest.java
new file mode 100644
index 0000000..07f7763
--- /dev/null
+++ b/suite/pts/deviceTests/browserbench/src/com/android/pts/browser/BrowserBenchTest.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.pts.browser;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.cts.util.TimeoutReq;
+import android.cts.util.WatchDog;
+import android.net.Uri;
+import android.provider.Browser;
+import android.util.Log;
+import android.webkit.cts.CtsTestServer;
+
+import com.android.pts.util.PtsAndroidTestCase;
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
+import com.android.pts.util.Stat;
+
+import java.net.URLDecoder;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.RequestLine;
+/**
+ * Browser benchmarking.
+ * It launches an activity with URL and wait for POST from the client.
+ */
+public class BrowserBenchTest extends PtsAndroidTestCase {
+    private static final String TAG = "BrowserBench";
+    private static final boolean DEBUG = false;
+    private static final String OCTANE_START_FILE = "octane/index.html";
+    private static final String ROBOHORNET_START_FILE = "robohornet/robohornet.html";
+    private static final String HOST_COMPLETION_BROADCAST = "com.android.pts.browser.completion";
+    // time-out for watch-dog. POST should happen within this time.
+    private static long BROWSER_POST_TIMEOUT_IN_MS = 10 * 60 * 1000L;
+    // watch-dog will time-out first. So make it long enough.
+    private static long BROWSER_COMPLETION_TIMEOUT_IN_MS = 60 * 60 * 1000L;
+    private static final String HTTP_USER_AGENT = "User-Agent";
+    private CtsTestServer mWebServer;
+    // used for final score
+    private ResultType mTypeNonFinal = ResultType.NEUTRAL;
+    private ResultUnit mUnitNonFinal = ResultUnit.NONE;
+    // used for all other scores
+    private ResultType mTypeFinal = ResultType.NEUTRAL;
+    private ResultUnit mUnitFinal = ResultUnit.SCORE;
+    private WatchDog mWatchDog;
+    private CountDownLatch mLatch;
+    // can be changed by each test before starting
+    private volatile int mNumberRepeat;
+    /** tells how many tests have run up to now */
+    private volatile int mRunIndex;
+    /** stores results for each runs. last entry will be the final score. */
+    private LinkedHashMap<String, double[]> mResultsMap;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mWebServer = new CtsTestServer(getContext()) {
+            @Override
+            protected HttpResponse onPost(HttpRequest request) throws Exception {
+                // post uri will look like "pts_report.html?final=1&score=10.1&message=hello"
+                RequestLine requestLine = request.getRequestLine();
+                String uriString = URLDecoder.decode(requestLine.getUri(), "UTF-8");
+                if (DEBUG) {
+                    Log.i(TAG, "uri:" + uriString);
+                }
+                String resultRe =
+                        ".*pts_report.html\\?final=([\\d])&score=([\\d]+\\.?[\\d]*)&message=([\\w][\\w ]*)";
+                Pattern resultPattern = Pattern.compile(resultRe);
+                Matcher matchResult = resultPattern.matcher(uriString);
+                if (matchResult.find()) {
+                    int isFinal = Integer.parseInt(matchResult.group(1));
+                    double score = Double.parseDouble(matchResult.group(2));
+                    String message = matchResult.group(3);
+                    Log.i(TAG, message + ":" + score);
+                    if (!mResultsMap.containsKey(message)) {
+                        mResultsMap.put(message, new double[mNumberRepeat]);
+                    }
+                    double[] scores = mResultsMap.get(message);
+                    scores[mRunIndex] = score;
+                    if (isFinal == 1) {
+                        String userAgent = request.getFirstHeader(HTTP_USER_AGENT).getValue();
+                        getReportLog().printValue(HTTP_USER_AGENT + "=" + userAgent, 0,
+                                ResultType.NEUTRAL, ResultUnit.NONE);
+                        mLatch.countDown();
+                    }
+                    mWatchDog.reset();
+                }
+                return null; // default response is OK as it will be ignored by client anyway.
+            }
+        };
+        mResultsMap = new LinkedHashMap<String, double[]>();
+        mWatchDog = new WatchDog(BROWSER_POST_TIMEOUT_IN_MS);
+        mWatchDog.start();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mWatchDog.stop();
+        mWebServer.shutdown();
+        mWebServer = null;
+        mResultsMap = null;
+        super.tearDown();
+    }
+
+    @TimeoutReq(minutes = 60)
+    public void testOctane() throws InterruptedException {
+        String url = mWebServer.getAssetUrl(OCTANE_START_FILE) + "?auto=1";
+        final int kRepeat = 5;
+        doTest(url, ResultType.LOWER_BETTER, ResultUnit.MS,
+                ResultType.HIGHER_BETTER, ResultUnit.SCORE, kRepeat);
+    }
+
+    private void doTest(String url, ResultType typeNonFinal, ResultUnit unitNonFinal,
+            ResultType typeFinal, ResultUnit unitFinal, int numberRepeat)
+                    throws InterruptedException {
+        mTypeNonFinal = typeNonFinal;
+        mUnitNonFinal = unitNonFinal;
+        mTypeFinal = typeFinal;
+        mUnitFinal = unitFinal;
+        mNumberRepeat = numberRepeat;
+        Uri uri = Uri.parse(url);
+        for (mRunIndex = 0; mRunIndex < numberRepeat; mRunIndex++) {
+            Log.i(TAG, mRunIndex + "-th round");
+            mLatch = new CountDownLatch(1);
+            Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            // force using only one window or tab
+            intent.putExtra(Browser.EXTRA_APPLICATION_ID, getContext().getPackageName());
+            getContext().startActivity(intent);
+            boolean ok = mLatch.await(BROWSER_COMPLETION_TIMEOUT_IN_MS, TimeUnit.MILLISECONDS);
+            assertTrue("timed-out", ok);
+        }
+        // it is somewhat awkward to handle the last one specially with Map
+        int numberEntries = mResultsMap.size();
+        int numberToProcess = 1;
+        for (Map.Entry<String, double[]> entry : mResultsMap.entrySet()) {
+            String message = entry.getKey();
+            double[] scores = entry.getValue();
+            if (numberToProcess == numberEntries) { // final score
+                // store the whole results first
+                getReportLog().printArray(message, scores, mTypeFinal, mUnitFinal);
+                getReportLog().printSummary(message, Stat.getAverage(scores), mTypeFinal,
+                        mUnitFinal);
+            } else { // interim results
+                getReportLog().printArray(message, scores, mTypeNonFinal, mUnitNonFinal);
+            }
+            numberToProcess++;
+        }
+    }
+}
diff --git a/suite/pts/deviceTests/dram/Android.mk b/suite/pts/deviceTests/dram/Android.mk
index dbe0ad3..47e5b52 100644
--- a/suite/pts/deviceTests/dram/Android.mk
+++ b/suite/pts/deviceTests/dram/Android.mk
@@ -30,6 +30,6 @@
 
 LOCAL_SDK_VERSION := 16
 
-include $(BUILD_PTS_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/suite/pts/deviceTests/dram/jni/MemoryNativeJni.cpp b/suite/pts/deviceTests/dram/jni/MemoryNativeJni.cpp
index 0c429b7..a54ab71 100644
--- a/suite/pts/deviceTests/dram/jni/MemoryNativeJni.cpp
+++ b/suite/pts/deviceTests/dram/jni/MemoryNativeJni.cpp
@@ -19,14 +19,14 @@
 #include <string.h>
 #include <sys/time.h>
 
-long currentTimeMillis()
+double currentTimeMillis()
 {
     struct timeval tv;
     gettimeofday(&tv, (struct timezone *) NULL);
-    return (long)tv.tv_sec * 1000 + tv.tv_usec / 1000;
+    return tv.tv_sec * 1000.0 + tv.tv_usec / 1000.0;
 }
 
-extern "C" JNIEXPORT jlong JNICALL Java_com_android_pts_dram_MemoryNative_runMemcpy(JNIEnv* env,
+extern "C" JNIEXPORT jdouble JNICALL Java_com_android_pts_dram_MemoryNative_runMemcpy(JNIEnv* env,
         jclass clazz, jint bufferSize, jint repetition)
 {
     char* src = new char[bufferSize];
@@ -39,13 +39,33 @@
     }
     memset(src, 0, bufferSize);
     memset(dst, 0, bufferSize);
-    long start = currentTimeMillis();
+    double start = currentTimeMillis();
     for (int i = 0; i < repetition; i++) {
         memcpy(dst, src, bufferSize);
         src[bufferSize - 1] = i & 0xff;
     }
-    long end = currentTimeMillis();
+    double end = currentTimeMillis();
     delete[] src;
     delete[] dst;
     return end - start;
 }
+
+extern "C" JNIEXPORT jdouble JNICALL Java_com_android_pts_dram_MemoryNative_runMemset(JNIEnv* env,
+        jclass clazz, jint bufferSize, jint repetition, jint c)
+{
+    char* dst = new char[bufferSize];
+    if (dst == NULL) {
+        delete[] dst;
+        env->ThrowNew(env->FindClass("java/lang/OutOfMemoryError"), "No memory");
+        return -1;
+    }
+    memset(dst, 0, bufferSize);
+    double start = currentTimeMillis();
+    for (int i = 0; i < repetition; i++) {
+        memset(dst, (c + i) & 0xff, bufferSize);
+    }
+    double end = currentTimeMillis();
+    delete[] dst;
+    return end - start;
+}
+
diff --git a/suite/pts/deviceTests/dram/src/com/android/pts/dram/BandwidthTest.java b/suite/pts/deviceTests/dram/src/com/android/pts/dram/BandwidthTest.java
index c731cc1..771a77c 100644
--- a/suite/pts/deviceTests/dram/src/com/android/pts/dram/BandwidthTest.java
+++ b/suite/pts/deviceTests/dram/src/com/android/pts/dram/BandwidthTest.java
@@ -21,6 +21,8 @@
 import android.util.Log;
 import android.view.WindowManager;
 
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
 import com.android.pts.util.PtsAndroidTestCase;
 import com.android.pts.util.ReportLog;
 import com.android.pts.util.Stat;
@@ -37,6 +39,14 @@
     private static final int REPEAT_IN_EACH_CALL = 100;
     private static final int KB = 1024;
     private static final int MB = 1024 * 1024;
+    private static final int MEMSET_CHAR = 0xa5;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        // warm-up
+        MemoryNative.runMemcpy(2 * MB, 100);
+    }
 
     public void testMemcpyK004() {
         doRunMemcpy(4 * KB);
@@ -90,15 +100,74 @@
         doRunMemcpy(16 * MB);
     }
 
+    public void testMemsetK004() {
+        doRunMemset(4 * KB);
+    }
+
+    public void testMemsetK008() {
+        doRunMemset(8 * KB);
+    }
+
+    public void testMemsetK016() {
+        doRunMemset(16 * KB);
+    }
+
+    public void testMemsetK032() {
+        doRunMemset(32 * KB);
+    }
+
+    public void testMemsetK064() {
+        doRunMemset(64 * KB);
+    }
+
+    public void testMemsetK128() {
+        doRunMemset(128 * KB);
+    }
+
+    public void testMemsetK256() {
+        doRunMemset(256 * KB);
+    }
+
+    public void testMemsetK512() {
+        doRunMemset(512 * KB);
+    }
+
+    public void testMemsetM001() {
+        doRunMemset(1 * MB);
+    }
+
+    public void testMemsetM002() {
+        doRunMemset(2 * MB);
+    }
+
+    public void testMemsetM004() {
+        doRunMemset(4 * MB);
+    }
+
+    public void testMemsetM008() {
+        doRunMemset(8 * MB);
+    }
+
+    public void testMemsetM016() {
+        doRunMemset(16 * MB);
+    }
+
     private void doRunMemcpy(int bufferSize) {
         double[] result = new double[REPETITION];
-        for (int i = 0; i < REPETITION; i++) {
-            result[i] = MemoryNative.runMemcpy(bufferSize, REPEAT_IN_EACH_CALL);
+        int repeatInEachCall = REPEAT_IN_EACH_CALL;
+        if (bufferSize < (1 * MB)) {
+            // too small buffer size finishes too early to give accurate result.
+            repeatInEachCall *= (1 * MB / bufferSize);
         }
-        getReportLog().printArray("ms", result, false);
+        for (int i = 0; i < REPETITION; i++) {
+            result[i] = MemoryNative.runMemcpy(bufferSize, repeatInEachCall);
+        }
+        getReportLog().printArray("memcpy time", result, ResultType.LOWER_BETTER,
+                ResultUnit.MS);
         double[] mbps = ReportLog.calcRatePerSecArray(
-                (double)bufferSize * REPEAT_IN_EACH_CALL / 1024.0 / 1024.0, result);
-        getReportLog().printArray("MB/s", mbps, true);
+                (double)bufferSize * repeatInEachCall / 1024.0 / 1024.0, result);
+        getReportLog().printArray("memcpy throughput", mbps, ResultType.HIGHER_BETTER,
+                ResultUnit.MBPS);
         Stat.StatResult stat = Stat.getStat(mbps);
         WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
         Point size = new Point();
@@ -107,8 +176,35 @@
         double pixels = size.x * size.y;
         // now this represents how many times the whole screen can be copied in a sec.
         double screensPerSecAverage = stat.mAverage / pixels * 1024.0 * 1024.0 / 4.0;
-        double screensPerSecStddev = stat.mStddev / pixels * 1024.0 * 1024.0 / 4.0;
-        getReportLog().printSummary("screen copies per sec", screensPerSecAverage,
-                screensPerSecStddev);
+        getReportLog().printSummary("memcpy in fps", screensPerSecAverage,
+                ResultType.HIGHER_BETTER, ResultUnit.FPS);
+    }
+
+    private void doRunMemset(int bufferSize) {
+        double[] result = new double[REPETITION];
+        int repeatInEachCall = REPEAT_IN_EACH_CALL;
+        if (bufferSize < (1 * MB)) {
+            // too small buffer size finishes too early to give accurate result.
+            repeatInEachCall *= (1 * MB / bufferSize);
+        }
+        for (int i = 0; i < REPETITION; i++) {
+            result[i] = MemoryNative.runMemset(bufferSize, repeatInEachCall, MEMSET_CHAR);
+        }
+        getReportLog().printArray("memset time", result, ResultType.LOWER_BETTER,
+                ResultUnit.MS);
+        double[] mbps = ReportLog.calcRatePerSecArray(
+                (double)bufferSize * repeatInEachCall / 1024.0 / 1024.0, result);
+        getReportLog().printArray("memset throughput", mbps, ResultType.HIGHER_BETTER,
+                ResultUnit.MBPS);
+        Stat.StatResult stat = Stat.getStat(mbps);
+        WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
+        Point size = new Point();
+        wm.getDefaultDisplay().getSize(size);
+        Log.i(TAG, " x " + size.x + " y " + size.y);
+        double pixels = size.x * size.y;
+        // now this represents how many times the whole screen can be copied in a sec.
+        double screensPerSecAverage = stat.mAverage / pixels * 1024.0 * 1024.0 / 4.0;
+        getReportLog().printSummary("memset in fps", screensPerSecAverage,
+                ResultType.HIGHER_BETTER, ResultUnit.FPS);
     }
 }
diff --git a/suite/pts/deviceTests/dram/src/com/android/pts/dram/MemoryNative.java b/suite/pts/deviceTests/dram/src/com/android/pts/dram/MemoryNative.java
index 991de1e..71b60a0 100644
--- a/suite/pts/deviceTests/dram/src/com/android/pts/dram/MemoryNative.java
+++ b/suite/pts/deviceTests/dram/src/com/android/pts/dram/MemoryNative.java
@@ -27,5 +27,15 @@
      * @param repeatition
      * @return time spent in copying in ms.
      */
-    public static native long runMemcpy(int bufferSize, int repetition);
+    public static native double runMemcpy(int bufferSize, int repetition);
+
+    /**
+     * run memset for given number of repetition from a source to a destination buffers
+     * with each having the size of bufferSize.
+     * @param bufferSize
+     * @param repetition
+     * @param c char to set. Only LSBs will be used to get char value.
+     * @return time spent in memset in ms.
+     */
+    public static native double runMemset(int bufferSize, int repetition, int c);
 }
diff --git a/suite/pts/deviceTests/filesystemperf/Android.mk b/suite/pts/deviceTests/filesystemperf/Android.mk
index b42d48e..ca7996c 100644
--- a/suite/pts/deviceTests/filesystemperf/Android.mk
+++ b/suite/pts/deviceTests/filesystemperf/Android.mk
@@ -28,5 +28,5 @@
 
 LOCAL_SDK_VERSION := 16
 
-include $(BUILD_PTS_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
 
diff --git a/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/FileUtil.java b/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/FileUtil.java
index 14fc45a..087cf0d 100644
--- a/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/FileUtil.java
+++ b/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/FileUtil.java
@@ -19,7 +19,6 @@
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStreamReader;
@@ -28,6 +27,8 @@
 
 import com.android.pts.util.MeasureRun;
 import com.android.pts.util.MeasureTime;
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
 import com.android.pts.util.ReportLog;
 import com.android.pts.util.Stat;
 import com.android.pts.util.SystemUtil;
@@ -298,12 +299,14 @@
         randomFile.close();
         double[] mbps = ReportLog.calcRatePerSecArray((double)fileSize / runsInOneGo / 1024 / 1024,
                 times);
-        report.printArray("MB/s",
-                mbps, true);
-        report.printArray("Rd amount", rdAmount, true);
+        report.printArray("read throughput",
+                mbps, ResultType.HIGHER_BETTER, ResultUnit.MBPS);
+        // This is just the amount of IO returned from kernel. So this is performance neutral.
+        report.printArray("read amount", rdAmount, ResultType.NEUTRAL, ResultUnit.BYTE);
         Stat.StatResult stat = Stat.getStat(mbps);
 
-        report.printSummary("MB/s", stat.mAverage, stat.mStddev);
+        report.printSummary("read throughput", stat.mAverage, ResultType.HIGHER_BETTER,
+                ResultUnit.MBPS);
     }
 
     /**
@@ -348,12 +351,14 @@
         randomFile.close();
         double[] mbps = ReportLog.calcRatePerSecArray((double)fileSize / runsInOneGo / 1024 / 1024,
                 times);
-        report.printArray("MB/s",
-                mbps, true);
-        report.printArray("Wr amount", wrAmount, true);
+        report.printArray("write throughput",
+                mbps, ResultType.HIGHER_BETTER, ResultUnit.MBPS);
+        report.printArray("write amount", wrAmount, ResultType.NEUTRAL,
+                ResultUnit.BYTE);
         Stat.StatResult stat = Stat.getStat(mbps);
 
-        report.printSummary("MB/s", stat.mAverage, stat.mStddev);
+        report.printSummary("write throughput", stat.mAverage, ResultType.HIGHER_BETTER,
+                ResultUnit.MBPS);
     }
 
     /**
@@ -386,11 +391,12 @@
             randomFile.close();
             double[] mbps = ReportLog.calcRatePerSecArray((double)bufferSize / 1024 / 1024,
                     times);
-            report.printArray(i + "-th round MB/s",
-                    mbps, true);
+            report.printArray(i + "-th round throughput",
+                    mbps, ResultType.HIGHER_BETTER, ResultUnit.MBPS);
             ReportLog.copyArray(mbps, mbpsAll, i * numberRepeatInOneRun);
         }
         Stat.StatResult stat = Stat.getStat(mbpsAll);
-        report.printSummary("MB/s", stat.mAverage, stat.mStddev);
+        report.printSummary("update throughput", stat.mAverage, ResultType.HIGHER_BETTER,
+                ResultUnit.MBPS);
     }
 }
diff --git a/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/SequentialRWTest.java b/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/SequentialRWTest.java
index 252c59e..4a9c3dd 100644
--- a/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/SequentialRWTest.java
+++ b/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/SequentialRWTest.java
@@ -19,6 +19,8 @@
 import android.cts.util.TimeoutReq;
 import com.android.pts.util.MeasureRun;
 import com.android.pts.util.MeasureTime;
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
 import com.android.pts.util.PtsAndroidTestCase;
 import com.android.pts.util.ReportLog;
 import com.android.pts.util.Stat;
@@ -47,7 +49,8 @@
     public void testSingleSequentialWrite() throws Exception {
         final int numberOfFiles =(int)(FileUtil.getFileSizeExceedingMemory(
                 getContext(), BUFFER_SIZE) / BUFFER_SIZE);
-        getReportLog().printValue("files", numberOfFiles);
+        getReportLog().printValue("files", numberOfFiles, ResultType.NEUTRAL,
+                ResultUnit.COUNT);
         final byte[] data = FileUtil.generateRandomData(BUFFER_SIZE);
         final File[] files = FileUtil.createNewFiles(getContext(), DIR_SEQ_WR,
                 numberOfFiles);
@@ -61,11 +64,13 @@
             }
         });
         double[] mbps = ReportLog.calcRatePerSecArray((double)BUFFER_SIZE / 1024 / 1024, times);
-        getReportLog().printArray("try " + numberOfFiles + " files, result MB/s",
-                mbps, true);
-        getReportLog().printArray("Wr amount", wrAmount, true);
+        getReportLog().printArray("write throughput",
+                mbps, ResultType.HIGHER_BETTER, ResultUnit.MBPS);
+        getReportLog().printArray("write amount", wrAmount, ResultType.NEUTRAL,
+                ResultUnit.BYTE);
         Stat.StatResult stat = Stat.getStat(mbps);
-        getReportLog().printSummary("MB/s", stat.mAverage, stat.mStddev);
+        getReportLog().printSummary("write throughput", stat.mAverage, ResultType.HIGHER_BETTER,
+                ResultUnit.MBPS);
     }
 
     @TimeoutReq(minutes = 60)
@@ -83,8 +88,9 @@
         final File file = FileUtil.createNewFilledFile(getContext(),
                 DIR_SEQ_RD, fileSize);
         long finish = System.currentTimeMillis();
-        getReportLog().printValue("write size " + fileSize + " result MB/s",
-                ReportLog.calcRatePerSec((double)fileSize / 1024 / 1024, finish - start));
+        getReportLog().printValue("write throughput for test file of length " + fileSize,
+                ReportLog.calcRatePerSec((double)fileSize / 1024 / 1024, finish - start),
+                ResultType.HIGHER_BETTER, ResultUnit.MBPS);
 
         final int NUMBER_READ = 10;
 
@@ -103,9 +109,10 @@
             }
         });
         double[] mbps = ReportLog.calcRatePerSecArray((double)fileSize / 1024 / 1024, times);
-        getReportLog().printArray("read MB/s",
-                mbps, true);
+        getReportLog().printArray("read throughput",
+                mbps, ResultType.HIGHER_BETTER, ResultUnit.MBPS);
         Stat.StatResult stat = Stat.getStat(mbps);
-        getReportLog().printSummary("MB/s", stat.mAverage, stat.mStddev);
+        getReportLog().printSummary("read throughput", stat.mAverage, ResultType.HIGHER_BETTER,
+                ResultUnit.MBPS);
     }
 }
diff --git a/suite/pts/hostTests/browser/browserlauncher/Android.mk b/suite/pts/deviceTests/opengl/Android.mk
similarity index 83%
copy from suite/pts/hostTests/browser/browserlauncher/Android.mk
copy to suite/pts/deviceTests/opengl/Android.mk
index 0592017..377562c 100644
--- a/suite/pts/hostTests/browser/browserlauncher/Android.mk
+++ b/suite/pts/deviceTests/opengl/Android.mk
@@ -20,14 +20,16 @@
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
-LOCAL_STATIC_JAVA_LIBRARIES := ptsutil ctsutil ctstestrunner ctstestserver
+LOCAL_STATIC_JAVA_LIBRARIES := ptsutil ctsutil ctstestrunner
+
+LOCAL_JNI_SHARED_LIBRARIES := libptsopengl_jni
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_PACKAGE_NAME := PtsDeviceBrowserLauncher
+LOCAL_PACKAGE_NAME := PtsDeviceOpenGl
 
 LOCAL_SDK_VERSION := 16
 
-include $(BUILD_PTS_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/suite/pts/deviceTests/opengl/AndroidManifest.xml b/suite/pts/deviceTests/opengl/AndroidManifest.xml
new file mode 100644
index 0000000..a91766f
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.pts.opengl"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <uses-sdk
+        android:minSdkVersion="16" />
+
+    <uses-feature
+        android:glEsVersion="0x00020000"
+        android:required="true" />
+
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />\
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+
+    <application android:allowBackup="false" >
+        <uses-library android:name="android.test.runner" />
+
+        <activity
+            android:name="com.android.pts.opengl.primitive.GLActivity"
+            android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+    <instrumentation
+        android:name="android.test.InstrumentationCtsTestRunner"
+        android:label="OpenGL ES 2.0 Benchmark"
+        android:targetPackage="com.android.pts.opengl" />
+
+</manifest>
\ No newline at end of file
diff --git a/suite/pts/hostTests/ptshostutil/Android.mk b/suite/pts/deviceTests/opengl/jni/Android.mk
similarity index 67%
copy from suite/pts/hostTests/ptshostutil/Android.mk
copy to suite/pts/deviceTests/opengl/jni/Android.mk
index 14e786a..b872b02 100644
--- a/suite/pts/hostTests/ptshostutil/Android.mk
+++ b/suite/pts/deviceTests/opengl/jni/Android.mk
@@ -11,19 +11,21 @@
 # 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.
-
+#
 LOCAL_PATH := $(call my-dir)
 
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed-prebuilt ddmlib-prebuilt junit
+LOCAL_MODULE := libptsopengl_jni
 
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_MODULE := ptshostutil
+# Get all cpp files but not hidden files
+LOCAL_SRC_FILES := $(patsubst ./%,%, $(shell cd $(LOCAL_PATH); \
+          find . -name "*.cpp" -and -not -name ".*"))
 
-include $(BUILD_HOST_JAVA_LIBRARY)
+LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
 
+LOCAL_SHARED_LIBRARIES := libEGL libGLESv2 libandroid libutils
+
+include $(BUILD_SHARED_LIBRARY)
\ No newline at end of file
diff --git a/suite/pts/deviceTests/opengl/jni/GLNative.cpp b/suite/pts/deviceTests/opengl/jni/GLNative.cpp
new file mode 100644
index 0000000..48b87a5
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/jni/GLNative.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+#include <jni.h>
+
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_android_pts_opengl_primitive_GLActivity_startBenchmark(
+        JNIEnv* env, jclass clazz, jint numFrames, jdoubleArray frameTimes) {
+    return true;
+}
+
+extern "C" JNIEXPORT void JNICALL
+Java_com_android_pts_opengl_primitive_GLActivity_setupFullPipelineBenchmark(
+        JNIEnv* env, jclass clazz, jobject surface, jint workload) {
+}
+
+extern "C" JNIEXPORT void JNICALL
+Java_com_android_pts_opengl_primitive_GLActivity_setupPixelOutputBenchmark(
+        JNIEnv* env, jclass clazz, jobject surface, jint workload) {
+}
+
+extern "C" JNIEXPORT void JNICALL
+Java_com_android_pts_opengl_primitive_GLActivity_setupShaderPerfBenchmark(
+        JNIEnv* env, jclass clazz, jobject surface, jint workload) {
+}
+
+extern "C" JNIEXPORT void JNICALL
+Java_com_android_pts_opengl_primitive_GLActivity_setupContextSwitchBenchmark(
+        JNIEnv* env, jclass clazz, jobject surface, jint workload) {
+}
diff --git a/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/primitive/Benchmark.java b/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/primitive/Benchmark.java
new file mode 100644
index 0000000..d61f45d
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/primitive/Benchmark.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.android.pts.opengl.primitive;
+
+/**
+ * Represents the different primitive benchmarks.
+ */
+public enum Benchmark {
+    FullPipeline,
+    PixelOutput,
+    ShaderPerf,
+    ContextSwitch;
+}
diff --git a/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/primitive/GLActivity.java b/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/primitive/GLActivity.java
new file mode 100644
index 0000000..0caa576
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/primitive/GLActivity.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.android.pts.opengl.primitive;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.cts.util.WatchDog;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.Surface;
+
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
+
+public class GLActivity extends Activity {
+
+    public final static String TAG = "GLActivity";
+    /**
+     * Holds the name of the benchmark to run.
+     */
+    public final static String INTENT_EXTRA_BENCHMARK_NAME = "benchmark_name";
+    /**
+     * Holds the number of milliseconds to wait before timing out.
+     */
+    public final static String INTENT_EXTRA_TIMEOUT = "timeout";
+    /**
+     * The minimum number of frames per second the device must achieve to pass.
+     */
+    public final static String INTENT_EXTRA_MIN_FPS = "min_fps";
+    /**
+     * The number of frames to render for each workload.
+     */
+    public final static String INTENT_EXTRA_NUM_FRAMES = "num_frames";
+
+    private Worker runner;
+    private volatile Exception mException;
+    private volatile Surface mSurface;
+
+    private Benchmark mBenchmark;
+    private int mTimeout;
+    private double mMinFps;
+    private int mNumFrames;
+    private volatile int mWorkload = 0;
+
+    @Override
+    public void onCreate(Bundle data) {
+        super.onCreate(data);
+        System.loadLibrary("ptsopengl_jni");
+        Intent intent = getIntent();
+        mBenchmark = Benchmark.valueOf(intent.getStringExtra(INTENT_EXTRA_BENCHMARK_NAME));
+        mTimeout = intent.getIntExtra(INTENT_EXTRA_TIMEOUT, 0);
+        mMinFps = intent.getDoubleExtra(INTENT_EXTRA_MIN_FPS, 0);
+        mNumFrames = intent.getIntExtra(INTENT_EXTRA_NUM_FRAMES, 0);
+
+        Log.i(TAG, "Benchmark: " + mBenchmark);
+        Log.i(TAG, "Time Out: " + mTimeout);
+        Log.i(TAG, "Min FPS: " + mMinFps);
+        Log.i(TAG, "Num Frames: " + mNumFrames);
+
+        SurfaceView surfaceView = new SurfaceView(this);
+        surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
+            @Override
+            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+                mSurface = holder.getSurface();
+            }
+
+            @Override
+            public void surfaceCreated(SurfaceHolder holder) {}
+
+            @Override
+            public void surfaceDestroyed(SurfaceHolder holder) {}
+        });
+        setContentView(surfaceView);
+    }
+
+    public int waitForCompletion() throws Exception {
+        // Creates, starts and waits for a worker to run the benchmark.
+        runner = new Worker();
+        runner.start();
+        runner.join();
+        if (mException != null) {
+            throw mException;
+        }
+        return mWorkload;
+    }
+
+    private static native void setupFullPipelineBenchmark(Surface surface, int workload);
+
+    private static native void setupPixelOutputBenchmark(Surface surface, int workload);
+
+    private static native void setupShaderPerfBenchmark(Surface surface, int workload);
+
+    private static native void setupContextSwitchBenchmark(Surface surface, int workload);
+
+    private static native boolean startBenchmark(int numFrames, double[] frameTimes);
+
+    /**
+     * This thread runs the benchmarks, freeing the UI thread.
+     */
+    private class Worker extends Thread {
+
+        private volatile boolean repeat = true;
+        private WatchDog watchDog;
+
+        @Override
+        public void run() {
+            // Creates a watchdog to ensure a iteration doesn't exceed the timeout.
+            watchDog = new WatchDog(mTimeout);
+            watchDog.start();
+            // Used to record the start and end time of the iteration.
+            double[] times = new double[2];
+            while (repeat) {
+                // The workload to use for this iteration.
+                int wl = mWorkload + 1;
+                // Setup the benchmark.
+                switch (mBenchmark) {
+                    case FullPipeline:
+                        setupFullPipelineBenchmark(mSurface, wl);
+                        break;
+                    case PixelOutput:
+                        setupPixelOutputBenchmark(mSurface, wl);
+                        break;
+                    case ShaderPerf:
+                        setupShaderPerfBenchmark(mSurface, wl);
+                        break;
+                    case ContextSwitch:
+                        setupContextSwitchBenchmark(mSurface, wl);
+                        break;
+                }
+                // Start benchmark.
+                if (!startBenchmark(mNumFrames, times)) {
+                    mException = new Exception("Could not run benchmark");
+                    repeat = false;
+                } else {
+                    // Calculate FPS.
+                    double totalTimeTaken = times[1] - times[0];
+                    double meanFps = mNumFrames * 1000.0f / totalTimeTaken;
+                    Log.i(TAG, "Workload: " + wl);
+                    Log.i(TAG, "MeanFPS: " + meanFps);
+                    if (meanFps >= mMinFps) {
+                        // Iteration passed, proceed to next one.
+                        mWorkload++;
+                    } else {
+                        // Iteration failed.
+                        repeat = false;
+                    }
+                }
+                // Resets the watchdog for the next iteration.
+                watchDog.reset();
+            }
+            watchDog.stop();
+        }
+
+    }
+}
diff --git a/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/primitive/GLBenchmark.java b/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/primitive/GLBenchmark.java
new file mode 100644
index 0000000..ad3159f
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/primitive/GLBenchmark.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.android.pts.opengl.primitive;
+
+import android.content.Intent;
+
+import com.android.pts.util.PtsActivityInstrumentationTestCase2;
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
+
+/**
+ * Runs the Primitive OpenGL ES 2.0 Benchmarks.
+ */
+public class GLBenchmark extends PtsActivityInstrumentationTestCase2<GLActivity> {
+
+    private static final double MIN_FPS = 50;
+
+    public GLBenchmark() {
+        super(GLActivity.class);
+    }
+
+    /**
+     * Runs the full OpenGL ES 2.0 pipeline test.
+     *
+     * @throws Exception If the benchmark could not be run.
+     */
+    public void testFullPipeline() throws Exception {
+        runBenchmark(Benchmark.FullPipeline, 500, 10000);
+    }
+
+    /**
+     * Runs the pixel output test.
+     *
+     * @throws Exception If the benchmark could not be run.
+     */
+    public void testPixelOutput() throws Exception {
+        runBenchmark(Benchmark.PixelOutput, 500, 10000);
+    }
+
+    /**
+     * Runs the shader performance test.
+     *
+     * @throws Exception If the benchmark could not be run.
+     */
+    public void testShaderPerf() throws Exception {
+        // TODO(stuartscott): Not yet implemented
+        // runBenchmark(Benchmark.ShaderPerf, 500, 10000);
+    }
+
+    /**
+     * Runs the OpenGL context switch overhead test.
+     *
+     * @throws Exception If the benchmark could not be run.
+     */
+    public void testContextSwitch() throws Exception {
+        runBenchmark(Benchmark.ContextSwitch, 500, 10000);
+    }
+
+    /**
+     * Runs the specified test.
+     *
+     * @param benchmark An enum representing the benchmark to run.
+     * @param numFrames The number of frames to render.
+     * @param timeout The milliseconds to wait for an iteration of the benchmark before timing out.
+     * @throws Exception If the benchmark could not be run.
+     */
+    private void runBenchmark(Benchmark benchmark, int numFrames, int timeout) throws Exception {
+        String benchmarkName = benchmark.toString();
+        Intent intent = new Intent();
+        intent.putExtra(GLActivity.INTENT_EXTRA_BENCHMARK_NAME, benchmarkName);
+        intent.putExtra(GLActivity.INTENT_EXTRA_TIMEOUT, timeout);
+        intent.putExtra(GLActivity.INTENT_EXTRA_MIN_FPS, MIN_FPS);
+        intent.putExtra(GLActivity.INTENT_EXTRA_NUM_FRAMES, numFrames);
+
+        GLActivity activity = null;
+        setActivityIntent(intent);
+        try {
+            activity = getActivity();
+            int workload = activity.waitForCompletion();
+            // represents the maximum workload it can do whilst maintaining MIN_FPS.
+            getReportLog()
+                    .printSummary("Workload", workload, ResultType.HIGHER_BETTER, ResultUnit.SCORE);
+        } finally {
+            if (activity != null) {
+                activity.finish();
+            }
+        }
+    }
+}
diff --git a/suite/pts/hostTests/browser/browserlauncher/Android.mk b/suite/pts/deviceTests/simplecpu/Android.mk
similarity index 83%
copy from suite/pts/hostTests/browser/browserlauncher/Android.mk
copy to suite/pts/deviceTests/simplecpu/Android.mk
index 0592017..69db471 100644
--- a/suite/pts/hostTests/browser/browserlauncher/Android.mk
+++ b/suite/pts/deviceTests/simplecpu/Android.mk
@@ -20,14 +20,16 @@
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
-LOCAL_STATIC_JAVA_LIBRARIES := ptsutil ctsutil ctstestrunner ctstestserver
+LOCAL_STATIC_JAVA_LIBRARIES := ptsutil ctsutil ctstestrunner
+
+LOCAL_JNI_SHARED_LIBRARIES := libptscpu_jni
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_PACKAGE_NAME := PtsDeviceBrowserLauncher
+LOCAL_PACKAGE_NAME := PtsDeviceSimpleCpu
 
 LOCAL_SDK_VERSION := 16
 
-include $(BUILD_PTS_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/suite/pts/deviceTests/simplecpu/AndroidManifest.xml b/suite/pts/deviceTests/simplecpu/AndroidManifest.xml
new file mode 100644
index 0000000..930bcbd
--- /dev/null
+++ b/suite/pts/deviceTests/simplecpu/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.pts.simplecpu">
+
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+            android:targetPackage="com.android.pts.simplecpu"
+            android:label="Vesy simple CPU benchmarking" />
+</manifest>
diff --git a/suite/pts/hostTests/ptshostutil/Android.mk b/suite/pts/deviceTests/simplecpu/jni/Android.mk
similarity index 76%
rename from suite/pts/hostTests/ptshostutil/Android.mk
rename to suite/pts/deviceTests/simplecpu/jni/Android.mk
index 14e786a..9c34e0e 100644
--- a/suite/pts/hostTests/ptshostutil/Android.mk
+++ b/suite/pts/deviceTests/simplecpu/jni/Android.mk
@@ -11,19 +11,21 @@
 # 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.
-
+#
 LOCAL_PATH := $(call my-dir)
 
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed-prebuilt ddmlib-prebuilt junit
+LOCAL_MODULE    := libptscpu_jni
 
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_MODULE := ptshostutil
+LOCAL_SRC_FILES := CpuNativeJni.cpp
 
-include $(BUILD_HOST_JAVA_LIBRARY)
+LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
 
+LOCAL_SHARED_LIBRARIES := libnativehelper
+
+LOCAL_SDK_VERSION := 14
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/suite/pts/deviceTests/simplecpu/jni/CpuNativeJni.cpp b/suite/pts/deviceTests/simplecpu/jni/CpuNativeJni.cpp
new file mode 100644
index 0000000..851f35e
--- /dev/null
+++ b/suite/pts/deviceTests/simplecpu/jni/CpuNativeJni.cpp
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jni.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+/* Code from now to qsort_local all copied from bionic source.
+ * The code is duplicated here to remove dependency on optimized bionic
+ */
+static __inline char    *med3(char *, char *, char *, int (*)(const void *, const void *));
+static __inline void     swapfunc(char *, char *, int, int);
+
+#define min(a, b)   (a) < (b) ? a : b
+
+/*
+ * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function".
+ */
+#define swapcode(TYPE, parmi, parmj, n) {       \
+    long i = (n) / sizeof (TYPE);           \
+    TYPE *pi = (TYPE *) (parmi);            \
+    TYPE *pj = (TYPE *) (parmj);            \
+    do {                        \
+        TYPE    t = *pi;            \
+        *pi++ = *pj;                \
+        *pj++ = t;              \
+        } while (--i > 0);              \
+}
+
+#define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \
+    es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1;
+
+static __inline void
+swapfunc(char *a, char *b, int n, int swaptype)
+{
+    if (swaptype <= 1)
+        swapcode(long, a, b, n)
+    else
+        swapcode(char, a, b, n)
+}
+
+#define swap(a, b)                  \
+    if (swaptype == 0) {                \
+        long t = *(long *)(a);          \
+        *(long *)(a) = *(long *)(b);        \
+        *(long *)(b) = t;           \
+    } else                      \
+        swapfunc(a, b, es, swaptype)
+
+#define vecswap(a, b, n)    if ((n) > 0) swapfunc(a, b, n, swaptype)
+
+static __inline char *
+med3(char *a, char *b, char *c, int (*cmp)(const void *, const void *))
+{
+    return cmp(a, b) < 0 ?
+           (cmp(b, c) < 0 ? b : (cmp(a, c) < 0 ? c : a ))
+              :(cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c ));
+}
+
+void
+qsort_local(void *aa, size_t n, size_t es, int (*cmp)(const void *, const void *))
+{
+    char *pa, *pb, *pc, *pd, *pl, *pm, *pn;
+    int d, r, swaptype, swap_cnt;
+    char *a = (char*)aa;
+
+loop:   SWAPINIT(a, es);
+    swap_cnt = 0;
+    if (n < 7) {
+        for (pm = (char *)a + es; pm < (char *) a + n * es; pm += es)
+            for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0;
+                 pl -= es)
+                swap(pl, pl - es);
+        return;
+    }
+    pm = (char *)a + (n / 2) * es;
+    if (n > 7) {
+        pl = (char *)a;
+        pn = (char *)a + (n - 1) * es;
+        if (n > 40) {
+            d = (n / 8) * es;
+            pl = med3(pl, pl + d, pl + 2 * d, cmp);
+            pm = med3(pm - d, pm, pm + d, cmp);
+            pn = med3(pn - 2 * d, pn - d, pn, cmp);
+        }
+        pm = med3(pl, pm, pn, cmp);
+    }
+    swap(a, pm);
+    pa = pb = (char *)a + es;
+
+    pc = pd = (char *)a + (n - 1) * es;
+    for (;;) {
+        while (pb <= pc && (r = cmp(pb, a)) <= 0) {
+            if (r == 0) {
+                swap_cnt = 1;
+                swap(pa, pb);
+                pa += es;
+            }
+            pb += es;
+        }
+        while (pb <= pc && (r = cmp(pc, a)) >= 0) {
+            if (r == 0) {
+                swap_cnt = 1;
+                swap(pc, pd);
+                pd -= es;
+            }
+            pc -= es;
+        }
+        if (pb > pc)
+            break;
+        swap(pb, pc);
+        swap_cnt = 1;
+        pb += es;
+        pc -= es;
+    }
+    if (swap_cnt == 0) {  /* Switch to insertion sort */
+        for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es)
+            for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0;
+                 pl -= es)
+                swap(pl, pl - es);
+        return;
+    }
+
+    pn = (char *)a + n * es;
+    r = min(pa - (char *)a, pb - pa);
+    vecswap(a, pb - r, r);
+    r = min(pd - pc, pn - pd - (int)es);
+    vecswap(pb, pn - r, r);
+    if ((r = pb - pa) > (int)es)
+        qsort_local(a, r / es, es, cmp);
+    if ((r = pd - pc) > (int)es) {
+        /* Iterate rather than recurse to save stack space */
+        a = pn - r;
+        n = r / es;
+        goto loop;
+    }
+    /* qsort(pn - r, r / es, es, cmp); */
+}
+
+/* code duplication ends here */
+
+/**
+ * Util for getting time stamp
+ */
+double currentTimeMillis()
+{
+    struct timeval tv;
+    gettimeofday(&tv, (struct timezone *) NULL);
+    return tv.tv_sec * 1000.0 + tv.tv_usec / 1000.0;
+}
+
+/**
+ * Initialize given array randomly for the given seed
+ */
+template <typename T> void randomInitArray(T* array, int len, unsigned int seed)
+{
+    srand(seed);
+    for (int i = 0; i < len; i++) {
+        array[i] = (T) rand();
+    }
+}
+
+/**
+ * comparison function for int, for qsort
+ */
+int cmpint(const void* p1, const void* p2)
+{
+    return *(int*)p1 - *(int*)p2;
+}
+
+extern "C" JNIEXPORT jdouble JNICALL Java_com_android_pts_simplecpu_CpuNative_runSort(JNIEnv* env,
+        jclass clazz, jint numberElements, jint repetition)
+{
+    int* data = new int[numberElements];
+    if (data == NULL) {
+        env->ThrowNew(env->FindClass("java/lang/OutOfMemoryError"), "No memory");
+        return -1;
+    }
+    double totalTime = 0;
+    for (int i = 0; i < repetition; i++) {
+        randomInitArray<int>(data, numberElements, 0);
+        double start = currentTimeMillis();
+        qsort_local(data, numberElements, sizeof(int), cmpint);
+        double end = currentTimeMillis();
+        totalTime += (end - start);
+    }
+    delete[] data;
+    return totalTime;
+}
+
+
+/**
+ * Do matrix multiplication, C = A x B with all matrices having dimension of n x n
+ * The implementation is not in the most efficient, but it is good enough for benchmarking purpose.
+ * @param n should be multiple of 8
+ */
+void doMatrixMultiplication(float* A, float* B, float* C, int n)
+{
+    // batch size
+    const int M = 8;
+    for (int i = 0; i < n; i++) {
+        for (int j = 0; j < n; j += M) {
+            float sum[M];
+            for (int k = 0; k < M; k++) {
+                sum[k] = 0;
+            }
+            // re-use the whole cache line for accessing B.
+            // otherwise, the whole line will be read and only one value will be used.
+
+            for (int k = 0; k < n; k++) {
+                float a = A[i * n + k];
+                sum[0] += a * B[k * n + j];
+                sum[1] += a * B[k * n + j + 1];
+                sum[2] += a * B[k * n + j + 2];
+                sum[3] += a * B[k * n + j + 3];
+                sum[4] += a * B[k * n + j + 4];
+                sum[5] += a * B[k * n + j + 5];
+                sum[6] += a * B[k * n + j + 6];
+                sum[7] += a * B[k * n + j + 7];
+            }
+            for (int k = 0; k < M; k++) {
+                C[i * n + j + k] = sum[k];
+            }
+        }
+    }
+}
+
+extern "C" JNIEXPORT jdouble JNICALL Java_com_android_pts_simplecpu_CpuNative_runMatrixMultiplication(
+        JNIEnv* env, jclass clazz, jint n, jint repetition)
+{
+    // C = A x B
+    float* A = new float[n * n];
+    float* B = new float[n * n];
+    float* C = new float[n * n];
+    if ((A == NULL) || (B == NULL) || (C == NULL)) {
+        delete[] A;
+        delete[] B;
+        delete[] C;
+        env->ThrowNew(env->FindClass("java/lang/OutOfMemoryError"), "No memory");
+        return -1;
+    }
+    double totalTime = 0;
+    for (int i = 0; i < repetition; i++) {
+        randomInitArray<float>(A, n * n, 0);
+        randomInitArray<float>(B, n * n, 1);
+        double start = currentTimeMillis();
+        doMatrixMultiplication(A, B, C, n);
+        double end = currentTimeMillis();
+        totalTime += (end - start);
+    }
+    delete[] A;
+    delete[] B;
+    delete[] C;
+    return totalTime;
+}
+
diff --git a/suite/pts/deviceTests/simplecpu/src/com/android/pts/simplecpu/CpuNative.java b/suite/pts/deviceTests/simplecpu/src/com/android/pts/simplecpu/CpuNative.java
new file mode 100644
index 0000000..43c4b50
--- /dev/null
+++ b/suite/pts/deviceTests/simplecpu/src/com/android/pts/simplecpu/CpuNative.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.pts.simplecpu;
+
+public class CpuNative {
+    static {
+        System.loadLibrary("ptscpu_jni");
+    }
+    /**
+     * run qsort for given number of repetition
+     * with each having the size of bufferSize.
+     * @param numberElements
+     * @param repeatition
+     * @return time taken for computation, added for all repetition in ms
+     */
+    public static native double runSort(int numberElements, int repetition);
+
+    /**
+     * run matrix multiplication of (n x n) x (n x n)
+     *
+     * @param n dimension, should be multiple of 8
+     * @param repetition
+     * @return time taken for computation, added for all repetition in ms
+     */
+    public static native double runMatrixMultiplication(int n, int repetition);
+}
diff --git a/suite/pts/deviceTests/simplecpu/src/com/android/pts/simplecpu/SimpleCpuTest.java b/suite/pts/deviceTests/simplecpu/src/com/android/pts/simplecpu/SimpleCpuTest.java
new file mode 100644
index 0000000..8b17171
--- /dev/null
+++ b/suite/pts/deviceTests/simplecpu/src/com/android/pts/simplecpu/SimpleCpuTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.pts.simplecpu;
+
+import android.cts.util.TimeoutReq;
+
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
+import com.android.pts.util.PtsAndroidTestCase;
+import com.android.pts.util.ReportLog;
+import com.android.pts.util.Stat;
+
+/**
+ * Very simple CPU benchmarking to check the basic capability of CPU.
+ * Cases include
+ *   qsort
+ *   matrix multiplication (for floating point performance)
+ */
+public class SimpleCpuTest extends PtsAndroidTestCase {
+    private static final String TAG = "BandwidthTest";
+    private static final int KB = 1024;
+    private static final int MB = 1024 * 1024;
+    private static final int NUMBER_REPEAT = 20;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        warmUpCpu();
+    }
+
+    public void testSort004KB() {
+        doTestSort(NUMBER_REPEAT, 4 * KB);
+    }
+
+    public void testSort128KB() {
+        doTestSort(NUMBER_REPEAT, 128 * KB);
+    }
+
+    public void testSort001MB() {
+        doTestSort(NUMBER_REPEAT, 1 * MB);
+    }
+
+    // will fit into L1
+    public void testMatrixMultiplication032() {
+        doMatrixMultiplication(NUMBER_REPEAT, 32);
+    }
+
+    // mostly fit into L2
+    public void testMatrixMultiplication128() {
+        doMatrixMultiplication(NUMBER_REPEAT, 128);
+    }
+
+    // may fit into L2
+    public void testMatrixMultiplication200() {
+        doMatrixMultiplication(NUMBER_REPEAT, 200);
+    }
+
+    public void testMatrixMultiplication400() {
+        doMatrixMultiplication(NUMBER_REPEAT, 400);
+    }
+
+    // will exceed L2
+    @TimeoutReq(minutes = 20)
+    public void testMatrixMultiplication600() {
+        doMatrixMultiplication(NUMBER_REPEAT, 600);
+    }
+
+    /**
+     * run some code to force full CPU freq.
+     */
+    private void warmUpCpu() {
+        CpuNative.runSort(1 * MB, 10);
+    }
+
+    /**
+     * qsort test
+     * @param numberRepeat
+     * @param arrayLength
+     */
+    private void doTestSort(int numberRepeat, int arrayLength) {
+        final int numberRepeatInEachCall = 10;
+        double[] result = new double[numberRepeat];
+        for (int i = 0; i < numberRepeat; i++) {
+            result[i] = CpuNative.runSort(arrayLength, numberRepeatInEachCall);
+        }
+        getReportLog().printArray("sorting time", result, ResultType.LOWER_BETTER,
+                ResultUnit.MS);
+        Stat.StatResult stat = Stat.getStat(result);
+        getReportLog().printSummary("sorting time", stat.mAverage, ResultType.LOWER_BETTER,
+                ResultUnit.MS);
+    }
+
+    /**
+     * Matrix multiplication test, nxn matrix multiplication
+     * @param numberRepeat
+     * @param n should be multiple of 8
+     */
+    private void doMatrixMultiplication(int numberRepeat, int n) {
+        assertTrue(n % 8 == 0);
+        final int numberRepeatInEachCall = 10;
+        double[] result = new double[numberRepeat];
+        for (int i = 0; i < numberRepeat; i++) {
+            result[i] = CpuNative.runMatrixMultiplication(n, numberRepeatInEachCall);
+        }
+        getReportLog().printArray("matrix mutiplication time", result, ResultType.LOWER_BETTER,
+                ResultUnit.MS);
+        Stat.StatResult stat = Stat.getStat(result);
+        getReportLog().printSummary("matrix mutiplication time", stat.mAverage,
+                ResultType.LOWER_BETTER, ResultUnit.MS);
+    }
+
+}
diff --git a/suite/pts/deviceTests/ui/Android.mk b/suite/pts/deviceTests/ui/Android.mk
index 28db8a1..3790342 100644
--- a/suite/pts/deviceTests/ui/Android.mk
+++ b/suite/pts/deviceTests/ui/Android.mk
@@ -24,12 +24,18 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := ptsutil ctsutil ctstestrunner
 
+LOCAL_JNI_SHARED_LIBRARIES := libctsopenglperf_jni
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
+# re-use existing openglperf code for CTS. Do not include any test from that dir.
+LOCAL_SRC_FILES += $(filter-out %Test.java,$(call all-java-files-under, ../../../../tests/tests/openglperf/src))
+# do not use its own assets for this package.
+LOCAL_ASSET_DIR := $(LOCAL_PATH)/../../../../tests/tests/openglperf/assets
 
 LOCAL_PACKAGE_NAME := PtsDeviceUi
 
 LOCAL_SDK_VERSION := 16
 
-include $(BUILD_PTS_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
 
 
diff --git a/suite/pts/deviceTests/ui/AndroidManifest.xml b/suite/pts/deviceTests/ui/AndroidManifest.xml
index b5e4937b..6925b5d 100644
--- a/suite/pts/deviceTests/ui/AndroidManifest.xml
+++ b/suite/pts/deviceTests/ui/AndroidManifest.xml
@@ -31,6 +31,7 @@
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity>
+         <activity android:name="android.openglperf.cts.GlPlanetsActivity" />
     </application>
     <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
             android:targetPackage="com.android.pts.ui"
diff --git a/suite/pts/deviceTests/ui/src/com/android/pts/ui/FpsTest.java b/suite/pts/deviceTests/ui/src/com/android/pts/ui/FpsTest.java
new file mode 100644
index 0000000..a2fd8d9
--- /dev/null
+++ b/suite/pts/deviceTests/ui/src/com/android/pts/ui/FpsTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.pts.ui;
+
+import android.content.Context;
+import android.content.Intent;
+import android.openglperf.cts.GlPlanetsActivity;
+import android.util.Log;
+import android.view.Display;
+import android.view.WindowManager;
+
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
+import com.android.pts.util.PtsActivityInstrumentationTestCase2;
+import com.android.pts.util.ReportLog;
+import com.android.pts.util.Stat;
+
+/**
+ * measure time taken to render n frames with OpenGL.
+ * This will measure the jankiness of the Gl rendering.
+ * If some frames are delayed, total time will take longer than n x refresh_rate
+ */
+public class FpsTest extends PtsActivityInstrumentationTestCase2<GlPlanetsActivity> {
+    private static final String TAG = "FpsTest";
+    private static final int NUM_FRAMES_TO_RENDER = 60 * 60;
+    private static final long RENDERING_TIMEOUT = NUM_FRAMES_TO_RENDER / 10;
+    // error of this much in ms in refresh interval is not considered as jankiness
+    // note that this margin is set to be little big to consider that there can be additional delay
+    // measurement on some devices showed many jankiness around 2 to 3 ms,
+    // which should not be counted
+    private static final double REFRESH_INTERVAL_THRESHHOLD_IN_MS = 4.0;
+    private GlPlanetsActivity mActivity;
+
+    public FpsTest() {
+        super(GlPlanetsActivity.class);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mActivity = null;
+        super.tearDown();
+    }
+
+    public void testFrameIntervals() throws Exception {
+        Intent intent = new Intent();
+        intent.putExtra(GlPlanetsActivity.INTENT_EXTRA_NUM_FRAMES,
+                NUM_FRAMES_TO_RENDER);
+        intent.putExtra(GlPlanetsActivity.INTENT_EXTRA_NUM_PLANETS, 0);
+        intent.putExtra(GlPlanetsActivity.INTENT_EXTRA_USE_VBO_VERTICES, true);
+        intent.putExtra(GlPlanetsActivity.INTENT_EXTRA_USE_VBO_INDICES, true);
+        intent.putExtra(GlPlanetsActivity.INTENT_EXTRA_NUM_INDEX_BUFFERS, 10);
+
+        setActivityIntent(intent);
+        mActivity = getActivity();
+        boolean waitResult = mActivity.waitForGlPlanetsCompletionWithTimeout(RENDERING_TIMEOUT);
+        assertTrue("timeout while waiting for rendering completion", waitResult);
+
+        int[] frameInterval = mActivity.getFrameInterval();
+        assertTrue(frameInterval.length == NUM_FRAMES_TO_RENDER);
+        double fpsMeasured = mActivity.getAverageFps();
+        WindowManager wm = (WindowManager)mActivity.getSystemService(Context.WINDOW_SERVICE);
+        Display dpy = wm.getDefaultDisplay();
+        double fpsNominal = dpy.getRefreshRate();
+        double frameIntervalNominalInMs = 1000.0 / fpsNominal;
+        int jankNumber = 0;
+        // only count positive ( = real delay)
+        double maxDelay = 0;
+        // first one not valid, and should be thrown away
+        double[] intervals = new double[NUM_FRAMES_TO_RENDER - 1];
+        double[] jankiness = new double[NUM_FRAMES_TO_RENDER - 1];
+        double deltaAccumulated = 0;
+        for (int i = 0; i < NUM_FRAMES_TO_RENDER - 1; i++) {
+            intervals[i] = frameInterval[i + 1];
+            double delay = (double)intervals[i] - frameIntervalNominalInMs;
+            if (Math.abs(delay) > REFRESH_INTERVAL_THRESHHOLD_IN_MS) {
+                // Falling here does not necessarily mean jank as it may be catching up
+                // the previous delay. Basically count the first delay as jank, but subsequent
+                // variation should be checked with accumulated delay
+                double delayAdjusted = 0;
+                if (deltaAccumulated == 0) { // This is the first delay. always consider as total
+                    jankNumber++;
+                    delayAdjusted = delay;
+                } else { // delay already happened
+                    double deltaFromLastRefresh = deltaAccumulated - Math.floor(deltaAccumulated /
+                            frameIntervalNominalInMs) * frameIntervalNominalInMs;
+                    if (deltaAccumulated < 0) {
+                        // adjust as the above operation makes delay positive
+                        deltaFromLastRefresh -= frameIntervalNominalInMs;
+                    }
+                    delayAdjusted = delay + deltaFromLastRefresh;
+                    if (Math.abs(delayAdjusted) > REFRESH_INTERVAL_THRESHHOLD_IN_MS) {
+                        jankNumber++;
+                    } else { // caught up
+                        delayAdjusted = 0;
+                        deltaAccumulated = 0;
+                    }
+                }
+                deltaAccumulated += delay;
+                if (delayAdjusted > maxDelay) {
+                    maxDelay = delayAdjusted;
+                }
+                jankiness[i] = delayAdjusted;
+            } else {
+                jankiness[i] = 0;
+                deltaAccumulated = 0;
+            }
+        }
+        Log.i(TAG, " fps nominal " + fpsNominal + " fps measured " + fpsMeasured);
+        getReportLog().printArray("intervals", intervals, ResultType.NEUTRAL,
+                ResultUnit.MS);
+        getReportLog().printArray("jankiness", jankiness, ResultType.LOWER_BETTER,
+                ResultUnit.MS);
+        getReportLog().printValue("number of jank", jankNumber, ResultType.LOWER_BETTER,
+                ResultUnit.COUNT);
+        getReportLog().printSummary("max jank delay", maxDelay, ResultType.LOWER_BETTER,
+                ResultUnit.MS);
+    }
+}
diff --git a/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingActivity.java b/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingActivity.java
index 7bdfff9..fbec65d 100644
--- a/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingActivity.java
+++ b/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingActivity.java
@@ -26,8 +26,10 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
-public class ScrollingActivity extends ListActivity implements OnScrollListener
-{
+/**
+ * Activity for measuring scrolling time of long list.
+ */
+public class ScrollingActivity extends ListActivity implements OnScrollListener {
     static final String TAG = "ScrollingActivity";
     private static final int NUMBER_ELEMENTS = 10000;
     private static final int SCROLL_TIME_IN_MS = 1;
@@ -36,8 +38,7 @@
     private CountDownLatch mLatchStop = null;
     private int mTargetLoc;
 
-    public void onCreate(Bundle icicle)
-    {
+    public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
         for (int i = 0; i < NUMBER_ELEMENTS; i++) {
             mItems[i] = Integer.toString(i);
@@ -46,7 +47,6 @@
                 android.R.layout.simple_list_item_1, mItems));
         ListView view = getListView();
         view.setOnScrollListener(this);
-        //view.setVelocityScale(100.0f);
     }
 
     public boolean scrollToTop() {
@@ -75,15 +75,6 @@
         mLatchStop = null;
         return result;
     }
-    public void onStop()
-    {
-        super.onStop();
-    }
-
-    public void onResume()
-    {
-        super.onResume();
-    }
 
     @Override
     public void onScrollStateChanged(AbsListView view, int scrollState) {
diff --git a/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingTest.java b/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingTest.java
index 8e4bbed..9fd43de 100644
--- a/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingTest.java
+++ b/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingTest.java
@@ -18,6 +18,8 @@
 import android.cts.util.TimeoutReq;
 import com.android.pts.util.MeasureRun;
 import com.android.pts.util.MeasureTime;
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
 import com.android.pts.util.PtsActivityInstrumentationTestCase2;
 import com.android.pts.util.ReportLog;
 import com.android.pts.util.Stat;
@@ -65,8 +67,10 @@
                 assertTrue(activity.scrollToTop());
             }
         });
-        getReportLog().printArray("ms", results, false);
+        getReportLog().printArray("scrolling time", results, ResultType.LOWER_BETTER,
+                ResultUnit.MS);
         Stat.StatResult stat = Stat.getStat(results);
-        getReportLog().printSummary("Time ms", stat.mAverage, stat.mStddev);
+        getReportLog().printSummary("scrolling time", stat.mAverage, ResultType.LOWER_BETTER,
+                ResultUnit.MS);
     }
 }
diff --git a/suite/pts/hostTests/browser/browserlauncher/Android.mk b/suite/pts/deviceTests/videoperf/Android.mk
similarity index 75%
copy from suite/pts/hostTests/browser/browserlauncher/Android.mk
copy to suite/pts/deviceTests/videoperf/Android.mk
index 0592017..055c9f7 100644
--- a/suite/pts/hostTests/browser/browserlauncher/Android.mk
+++ b/suite/pts/deviceTests/videoperf/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2012 The Android Open Source Project
+# Copyright (C) 2013 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.
@@ -20,14 +20,13 @@
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
-LOCAL_STATIC_JAVA_LIBRARIES := ptsutil ctsutil ctstestrunner ctstestserver
+LOCAL_STATIC_JAVA_LIBRARIES := ptsutil ctsutil ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_PACKAGE_NAME := PtsDeviceBrowserLauncher
+LOCAL_PACKAGE_NAME := PtsDeviceVideoPerf
 
 LOCAL_SDK_VERSION := 16
 
-include $(BUILD_PTS_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
 
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/suite/pts/deviceTests/videoperf/AndroidManifest.xml b/suite/pts/deviceTests/videoperf/AndroidManifest.xml
new file mode 100644
index 0000000..fdef1ef
--- /dev/null
+++ b/suite/pts/deviceTests/videoperf/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.pts.videoperf">
+
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+            android:targetPackage="com.android.pts.videoperf"
+            android:label="UI Latency measurement" />
+</manifest>
diff --git a/suite/pts/deviceTests/videoperf/src/com/android/pts/videoperf/VideoEncoderDecoderTest.java b/suite/pts/deviceTests/videoperf/src/com/android/pts/videoperf/VideoEncoderDecoderTest.java
new file mode 100644
index 0000000..b5ed9c2
--- /dev/null
+++ b/suite/pts/deviceTests/videoperf/src/com/android/pts/videoperf/VideoEncoderDecoderTest.java
@@ -0,0 +1,779 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.pts.videoperf;
+
+import android.graphics.Point;
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecInfo.CodecProfileLevel;
+import android.media.MediaCodecList;
+import android.media.MediaFormat;
+import android.util.Log;
+
+import com.android.pts.util.PtsAndroidTestCase;
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
+import com.android.pts.util.Stat;
+
+import java.nio.ByteBuffer;
+import java.lang.System;
+import java.util.Random;
+import java.util.Vector;
+
+/**
+ * This tries to test video encoder / decoder performance by running encoding / decoding
+ * without displaying the raw data. To make things simpler, encoder is used to encode synthetic
+ * data and decoder is used to decode the encoded video. This approach does not work where
+ * there is only decoder. Performance index is total time taken for encoding and decoding
+ * the whole frames.
+ * To prevent sacrificing quality for faster encoding / decoding, randomly selected pixels are
+ * compared with the original image. As the pixel comparison can slow down the decoding process,
+ * only some randomly selected pixels are compared. As there can be only one performance index,
+ * error above certain threshhold in pixel value will be treated as an error.
+ */
+public class VideoEncoderDecoderTest extends PtsAndroidTestCase {
+    private static final String TAG = "VideoEncoderDecoderTest";
+    // this wait time affects fps as too big value will work as a blocker if device fps
+    // is not very high.
+    private static final long VIDEO_CODEC_WAIT_TIME_US = 5000;
+    private static final boolean VERBOSE = false;
+    private static final String VIDEO_AVC = "video/avc";
+    private static final int TOTAL_FRAMES = 300;
+    private static final int NUMBER_OF_REPEAT = 10;
+    // i frame interval for encoder
+    private static final int KEY_I_FRAME_INTERVAL = 5;
+
+    private static final int Y_CLAMP_MIN = 16;
+    private static final int Y_CLAMP_MAX = 235;
+    private static final int YUV_PLANE_ADDITIONAL_LENGTH = 200;
+    private ByteBuffer mYBuffer;
+    private ByteBuffer mUVBuffer;
+    // if input raw data is semi-planar
+    private boolean mSrcSemiPlanar;
+    // if output raw data is semi-planar
+    private boolean mDstSemiPlanar;
+    private int mBufferWidth;
+    private int mBufferHeight;
+    private int mVideoWidth;
+    private int mVideoHeight;
+
+    private Vector<ByteBuffer> mEncodedOutputBuffer;
+    // check this many pixels per each decoded frame
+    // checking too many points decreases decoder frame rates a lot.
+    private static final int PIXEL_CHECK_PER_FRAME = 1000;
+    // RMS error in pixel values above this will be treated as error.
+    private static final double PIXEL_RMS_ERROR_MARGAIN = 20.0;
+    private Random mRandom;
+
+    @Override
+    protected void setUp() throws Exception {
+        mEncodedOutputBuffer = new Vector<ByteBuffer>(TOTAL_FRAMES * 2);
+        // Use time as a seed, hoping to prevent checking pixels in the same pattern
+        long now = System.currentTimeMillis();
+        mRandom = new Random(now);
+        super.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mEncodedOutputBuffer.clear();
+        mEncodedOutputBuffer = null;
+        mYBuffer = null;
+        mUVBuffer = null;
+        mRandom = null;
+        super.tearDown();
+    }
+
+    public void testAvc0176x0144() throws Exception {
+        doTest(VIDEO_AVC, 176, 144, NUMBER_OF_REPEAT);
+    }
+
+    public void testAvc0352x0288() throws Exception {
+        doTest(VIDEO_AVC, 352, 288, NUMBER_OF_REPEAT);
+    }
+
+    public void testAvc0720x0480() throws Exception {
+        doTest(VIDEO_AVC, 720, 480, NUMBER_OF_REPEAT);
+    }
+
+    public void testAvc1280x0720() throws Exception {
+        doTest(VIDEO_AVC, 1280, 720, NUMBER_OF_REPEAT);
+    }
+
+    /**
+     * resolution intentionally set to 1072 not 1080
+     * as 1080 is not multiple of 16, and it requires additional setting like stride
+     * which is not specified in API documentation.
+     */
+    public void testAvc1920x1072() throws Exception {
+        doTest(VIDEO_AVC, 1920, 1072, NUMBER_OF_REPEAT);
+    }
+
+    /**
+     * Used to pass codec information for given codec type / width / height
+     */
+    private class CodecInfo {
+        public int mBitRate = 0;
+        public int mFps = 0;
+        public boolean mSupportSemiPlanar = false;
+        public boolean mSupportPlanar = false;
+    }
+
+    /**
+     * Check if given codec with given (w,h) is supported.
+     * @param mimeType codec type in mime format like "video/avc"
+     * @param w video width
+     * @param h video height
+     * @param isEncoder whether the codec is encoder or decoder
+     * @return null if the configuration is not supported.
+     */
+    private CodecInfo getSupportedFormatInfo(String mimeType, int w, int h, boolean isEncoder) {
+        CodecCapabilities cap = getCodecCapability(mimeType, isEncoder);
+        if (cap == null) { // not supported
+            return null;
+        }
+        CodecInfo info = new CodecInfo();
+        for (int color : cap.colorFormats) {
+            if (color == CodecCapabilities.COLOR_FormatYUV420SemiPlanar) {
+                info.mSupportSemiPlanar = true;
+            }
+            if (color == CodecCapabilities.COLOR_FormatYUV420Planar) {
+                info.mSupportPlanar = true;
+            }
+        }
+        printIntArray("supported colors", cap.colorFormats);
+        //  either YUV420 planar or semiplanar should be supported
+        if (!info.mSupportPlanar && !info.mSupportSemiPlanar) {
+            Log.i(TAG, "no supported color format");
+            return null;
+        }
+
+        if (mimeType.equals(VIDEO_AVC)) {
+            int highestLevel = 0;
+            for (CodecProfileLevel lvl : cap.profileLevels) {
+                if (lvl.level > highestLevel) {
+                    highestLevel = lvl.level;
+                }
+            }
+            Log.i(TAG, "Avc highest level " + Integer.toHexString(highestLevel));
+            int maxW = 0;
+            int maxH = 0;
+            int bitRate = 0;
+            double fps = 0; // frame rate for the max resolution
+            switch(highestLevel) {
+            // Do not support Level 1 to 2.
+            case CodecProfileLevel.AVCLevel1:
+            case CodecProfileLevel.AVCLevel11:
+            case CodecProfileLevel.AVCLevel12:
+            case CodecProfileLevel.AVCLevel13:
+            case CodecProfileLevel.AVCLevel1b:
+            case CodecProfileLevel.AVCLevel2:
+                fail();
+                break;
+            case CodecProfileLevel.AVCLevel21:
+                maxW = 352;
+                maxH = 576;
+                bitRate = 4000000;
+                fps = 25;
+                break;
+            case CodecProfileLevel.AVCLevel22:
+                maxW = 720;
+                maxH = 480;
+                bitRate = 4000000;
+                fps = 15;
+                break;
+            case CodecProfileLevel.AVCLevel3:
+                maxW = 720;
+                maxH = 480;
+                bitRate = 10000000;
+                fps = 30;
+                break;
+            case CodecProfileLevel.AVCLevel31:
+                maxW = 1280;
+                maxH = 720;
+                bitRate = 14000000;
+                fps = 30;
+                break;
+            case CodecProfileLevel.AVCLevel32:
+                maxW = 1280;
+                maxH = 720;
+                bitRate = 20000000;
+                fps = 60;
+                break;
+            case CodecProfileLevel.AVCLevel4:
+                maxW = 1920;
+                maxH = 1080;
+                bitRate = 20000000;
+                fps = 30.1;
+                break;
+            case CodecProfileLevel.AVCLevel41:
+                maxW = 1920;
+                maxH = 1080;
+                bitRate = 50000000;
+                fps = 30.1;
+                break;
+            case CodecProfileLevel.AVCLevel42:
+                maxW = 2048;
+                maxH = 1080;
+                bitRate = 50000000;
+                fps = 60;
+                break;
+            case CodecProfileLevel.AVCLevel5:
+                maxW = 3672;
+                maxH = 1536;
+                bitRate = 135000000;
+                fps = 26.7;
+                break;
+            case CodecProfileLevel.AVCLevel51:
+                maxW = 4096;
+                maxH = 2304;
+                bitRate = 240000000;
+                fps = 26.7;
+                break;
+            default:
+                maxW = 4096;
+                maxH = 2304;
+                bitRate = 240000000;
+                fps = 26.7;
+                break;
+            }
+            if ((w > maxW) || (h > maxH)) {
+                Log.i(TAG, "Requested resolution (" + w + "," + h + ") exceeds (" +
+                        maxW + "," + maxH + ")");
+                return null;
+            }
+            info.mFps = (int)(fps * maxW * maxH / (w * h));
+            info.mBitRate = bitRate;
+            Log.i(TAG, "AVC Level " + Integer.toHexString(highestLevel) + " bit rate " + bitRate +
+                    " fps " + info.mFps);
+        }
+        return info;
+    }
+
+    /**
+     * Run encoding / decoding test for given mimeType of codec
+     * @param mimeType like video/avc
+     * @param w video width
+     * @param h video height
+     * @param numberRepeat how many times to repeat the encoding / decoding process
+     */
+    private void doTest(String mimeType, int w, int h, int numberRepeat) throws Exception {
+        CodecInfo infoEnc = getSupportedFormatInfo(mimeType, w, h, true);
+        if (infoEnc == null) {
+            Log.i(TAG, "Codec " + mimeType + "with " + w + "," + h + " not supported");
+            return;
+        }
+        CodecInfo infoDec = getSupportedFormatInfo(mimeType, w, h, false);
+        assertNotNull(infoDec);
+        mVideoWidth = w;
+        mVideoHeight = h;
+        initYUVPlane(w + YUV_PLANE_ADDITIONAL_LENGTH, h + YUV_PLANE_ADDITIONAL_LENGTH,
+                infoEnc.mSupportSemiPlanar, infoDec.mSupportSemiPlanar);
+        double[] encoderFpsResults = new double[numberRepeat];
+        double[] decoderFpsResults = new double[numberRepeat];
+        double[] totalFpsResults = new double[numberRepeat];
+        double[] decoderRmsErrorResults = new double[numberRepeat];
+        for (int i = 0; i < numberRepeat; i++) {
+            MediaFormat format = new MediaFormat();
+            format.setString(MediaFormat.KEY_MIME, mimeType);
+            format.setInteger(MediaFormat.KEY_BIT_RATE, infoEnc.mBitRate);
+            format.setInteger(MediaFormat.KEY_WIDTH, w);
+            format.setInteger(MediaFormat.KEY_HEIGHT, h);
+            format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
+                    infoEnc.mSupportSemiPlanar ? CodecCapabilities.COLOR_FormatYUV420SemiPlanar :
+                        CodecCapabilities.COLOR_FormatYUV420Planar);
+            format.setInteger(MediaFormat.KEY_FRAME_RATE, infoEnc.mFps);
+            format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, KEY_I_FRAME_INTERVAL);
+            double encodingTime = runEncoder(VIDEO_AVC, format, TOTAL_FRAMES);
+            // re-initialize format for decoder
+            format = new MediaFormat();
+            format.setString(MediaFormat.KEY_MIME, mimeType);
+            format.setInteger(MediaFormat.KEY_WIDTH, w);
+            format.setInteger(MediaFormat.KEY_HEIGHT, h);
+            format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
+                    infoDec.mSupportSemiPlanar ? CodecCapabilities.COLOR_FormatYUV420SemiPlanar :
+                        CodecCapabilities.COLOR_FormatYUV420Planar);
+            double[] decoderResult = runDecoder(VIDEO_AVC, format);
+            double decodingTime = decoderResult[0];
+            decoderRmsErrorResults[i] = decoderResult[1];
+            encoderFpsResults[i] = (double)TOTAL_FRAMES / encodingTime * 1000.0;
+            decoderFpsResults[i] = (double)TOTAL_FRAMES / decodingTime * 1000.0;
+            totalFpsResults[i] = (double)TOTAL_FRAMES / (encodingTime + decodingTime) * 1000.0;
+
+            // clear things for re-start
+            mEncodedOutputBuffer.clear();
+            // it will be good to clean everything to make every run the same.
+            System.gc();
+        }
+        getReportLog().printArray("encoder", encoderFpsResults, ResultType.HIGHER_BETTER,
+                ResultUnit.FPS);
+        getReportLog().printArray("rms error", decoderRmsErrorResults, ResultType.LOWER_BETTER,
+                ResultUnit.NONE);
+        getReportLog().printArray("decoder", decoderFpsResults, ResultType.HIGHER_BETTER,
+                ResultUnit.FPS);
+        getReportLog().printArray("encoder decoder", totalFpsResults, ResultType.HIGHER_BETTER,
+                ResultUnit.FPS);
+        getReportLog().printSummary("encoder decoder", Stat.getAverage(totalFpsResults),
+                ResultType.HIGHER_BETTER, ResultUnit.FPS);
+        // make sure that rms error is not too big.
+        for (int i = 0; i < numberRepeat; i++) {
+            assertTrue(decoderRmsErrorResults[i] < PIXEL_RMS_ERROR_MARGAIN);
+        }
+    }
+
+    /**
+     * run encoder benchmarking
+     * @param mimeType encoder type like video/avc
+     * @param format format of media to encode
+     * @param totalFrames total number of frames to encode
+     * @return time taken in ms to encode the frames. This does not include initialization time.
+     */
+    private double runEncoder(String mimeType, MediaFormat format, int totalFrames) {
+        MediaCodec codec = MediaCodec.createEncoderByType(mimeType);
+        try {
+            codec.configure(
+                    format,
+                    null /* surface */,
+                    null /* crypto */,
+                    MediaCodec.CONFIGURE_FLAG_ENCODE);
+        } catch (IllegalStateException e) {
+            Log.e(TAG, "codec '" + mimeType + "' failed configuration.");
+            assertTrue("codec '" + mimeType + "' failed configuration.", false);
+        }
+        codec.start();
+        ByteBuffer[] codecInputBuffers = codec.getInputBuffers();
+        ByteBuffer[] codecOutputBuffers = codec.getOutputBuffers();
+
+        int numBytesSubmitted = 0;
+        int numBytesDequeued = 0;
+        int inFramesCount = 0;
+        long start = System.currentTimeMillis();
+        while (true) {
+            int index;
+
+            if (inFramesCount < totalFrames) {
+                index = codec.dequeueInputBuffer(VIDEO_CODEC_WAIT_TIME_US /* timeoutUs */);
+                if (index != MediaCodec.INFO_TRY_AGAIN_LATER) {
+                    int size = queueInputBufferEncoder(
+                            codec, codecInputBuffers, index, inFramesCount,
+                            (inFramesCount == (totalFrames - 1)) ?
+                                    MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
+                    inFramesCount++;
+                    numBytesSubmitted += size;
+                    if (VERBOSE) {
+                        Log.d(TAG, "queued " + size + " bytes of input data, frame " +
+                                (inFramesCount - 1));
+                    }
+
+                }
+            }
+            MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+            index = codec.dequeueOutputBuffer(info, VIDEO_CODEC_WAIT_TIME_US /* timeoutUs */);
+            if (index == MediaCodec.INFO_TRY_AGAIN_LATER) {
+            } else if (index == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+            } else if (index == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+                codecOutputBuffers = codec.getOutputBuffers();
+            } else if (index >= 0) {
+                dequeueOutputBufferEncoder(codec, codecOutputBuffers, index, info);
+                numBytesDequeued += info.size;
+                if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                    if (VERBOSE) {
+                        Log.d(TAG, "dequeued output EOS.");
+                    }
+                    break;
+                }
+                if (VERBOSE) {
+                    Log.d(TAG, "dequeued " + info.size + " bytes of output data.");
+                }
+            }
+        }
+        long finish = System.currentTimeMillis();
+        if (VERBOSE) {
+            Log.d(TAG, "queued a total of " + numBytesSubmitted + "bytes, "
+                    + "dequeued " + numBytesDequeued + " bytes.");
+        }
+        codec.stop();
+        codec.release();
+        codec = null;
+        return (double)(finish - start);
+    }
+
+    /**
+     * Fills input buffer for encoder from YUV buffers.
+     * @return size of enqueued data.
+     */
+    private int queueInputBufferEncoder(
+            MediaCodec codec, ByteBuffer[] inputBuffers, int index, int frameCount, int flags) {
+        ByteBuffer buffer = inputBuffers[index];
+        buffer.clear();
+
+        Point origin = getOrigin(frameCount);
+        // Y color first
+        int srcOffsetY = origin.x + origin.y * mBufferWidth;
+        final byte[] yBuffer = mYBuffer.array();
+        for (int i = 0; i < mVideoHeight; i++) {
+            buffer.put(yBuffer, srcOffsetY, mVideoWidth);
+            srcOffsetY += mBufferWidth;
+        }
+        if (mSrcSemiPlanar) {
+            int srcOffsetU = origin.y / 2 * mBufferWidth + origin.x / 2 * 2;
+            final byte[] uvBuffer = mUVBuffer.array();
+            for (int i = 0; i < mVideoHeight / 2; i++) {
+                buffer.put(uvBuffer, srcOffsetU, mVideoWidth);
+                srcOffsetU += mBufferWidth;
+            }
+        } else {
+            int srcOffsetU = origin.y / 2 * mBufferWidth / 2 + origin.x / 2;
+            int srcOffsetV = srcOffsetU + mBufferWidth / 2 * mBufferHeight / 2;
+            final byte[] uvBuffer = mUVBuffer.array();
+            for (int i = 0; i < mVideoHeight /2; i++) { //U only
+                buffer.put(uvBuffer, srcOffsetU, mVideoWidth / 2);
+                srcOffsetU += mBufferWidth / 2;
+            }
+            for (int i = 0; i < mVideoHeight /2; i++) { //V only
+                buffer.put(uvBuffer, srcOffsetV, mVideoWidth / 2);
+                srcOffsetV += mBufferWidth / 2;
+            }
+        }
+        int size = mVideoHeight * mVideoWidth * 3 /2;
+
+        codec.queueInputBuffer(index, 0 /* offset */, size, 0 /* timeUs */, flags);
+        if (VERBOSE && (frameCount == 0)) {
+            printByteArray("Y ", mYBuffer.array(), 0, 20);
+            printByteArray("UV ", mUVBuffer.array(), 0, 20);
+            printByteArray("UV ", mUVBuffer.array(), mBufferWidth * 60, 20);
+        }
+        return size;
+    }
+
+    /**
+     * Dequeue encoded data from output buffer and store for later usage.
+     */
+    private void dequeueOutputBufferEncoder(
+            MediaCodec codec, ByteBuffer[] outputBuffers,
+            int index, MediaCodec.BufferInfo info) {
+        ByteBuffer output = outputBuffers[index];
+        output.clear();
+        int l = info.size;
+        ByteBuffer copied = ByteBuffer.allocate(l);
+        output.get(copied.array(), 0, l);
+        mEncodedOutputBuffer.add(copied);
+        codec.releaseOutputBuffer(index, false /* render */);
+    }
+
+    /**
+     * run encoder benchmarking with encoded stream stored from encoding phase
+     * @param mimeType encoder type like video/avc
+     * @param format format of media to decode
+     * @return returns length-2 array with 0: time for decoding, 1 : rms error of pixels
+     */
+    private double[] runDecoder(String mimeType, MediaFormat format) {
+        MediaCodec codec = MediaCodec.createDecoderByType(mimeType);
+        codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
+        codec.start();
+        ByteBuffer[] codecInputBuffers = codec.getInputBuffers();
+        ByteBuffer[] codecOutputBuffers = codec.getOutputBuffers();
+
+        double totalErrorSquared = 0;
+
+        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+        boolean sawOutputEOS = false;
+        int inputLeft = mEncodedOutputBuffer.size();
+        int inputBufferCount = 0;
+        int outFrameCount = 0;
+        YUVValue expected = new YUVValue();
+        YUVValue decoded = new YUVValue();
+        long start = System.currentTimeMillis();
+        while (!sawOutputEOS) {
+            if (inputLeft > 0) {
+                int inputBufIndex = codec.dequeueInputBuffer(VIDEO_CODEC_WAIT_TIME_US);
+
+                if (inputBufIndex >= 0) {
+                    ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
+                    dstBuf.clear();
+                    ByteBuffer src = mEncodedOutputBuffer.get(inputBufferCount);
+                    int writeSize = src.capacity();
+                    dstBuf.put(src.array(), 0, writeSize);
+                    codec.queueInputBuffer(
+                            inputBufIndex,
+                            0 /* offset */,
+                            writeSize,
+                            0,
+                            (inputLeft == 1) ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
+                    inputLeft --;
+                    inputBufferCount ++;
+                }
+            }
+
+            int res = codec.dequeueOutputBuffer(info, VIDEO_CODEC_WAIT_TIME_US);
+            if (res >= 0) {
+                int outputBufIndex = res;
+                ByteBuffer buf = codecOutputBuffers[outputBufIndex];
+                if (VERBOSE && (outFrameCount == 0)) {
+                    printByteBuffer("Y ", buf, 0, 20);
+                    printByteBuffer("UV ", buf, mVideoWidth * mVideoHeight, 20);
+                    printByteBuffer("UV ", buf, mVideoWidth * mVideoHeight + mVideoWidth * 60, 20);
+                }
+                Point origin = getOrigin(outFrameCount);
+                for (int i = 0; i < PIXEL_CHECK_PER_FRAME; i++) {
+                    int w = mRandom.nextInt(mVideoWidth);
+                    int h = mRandom.nextInt(mVideoHeight);
+                    getPixelValuesFromYUVBuffers(origin.x, origin.y, w, h, expected);
+                    getPixelValuesFromOutputBuffer(buf, w, h, decoded);
+                    if (VERBOSE) {
+                        Log.i(TAG, outFrameCount + "-" + i + "- th round expcted " + expected.mY +
+                                "," + expected.mU + "," + expected.mV + "  decoded " + decoded.mY +
+                                "," + decoded.mU + "," + decoded.mV);
+                    }
+                    totalErrorSquared += expected.calcErrorSquared(decoded);
+                }
+                codec.releaseOutputBuffer(outputBufIndex, false /* render */);
+                if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                    Log.d(TAG, "saw output EOS.");
+                    sawOutputEOS = true;
+                }
+                outFrameCount++;
+            } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+                codecOutputBuffers = codec.getOutputBuffers();
+                Log.d(TAG, "output buffers have changed.");
+            } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+                MediaFormat oformat = codec.getOutputFormat();
+                Log.d(TAG, "output format has changed to " + oformat);
+                int colorFormat = oformat.getInteger(MediaFormat.KEY_COLOR_FORMAT);
+                if (colorFormat == CodecCapabilities.COLOR_FormatYUV420SemiPlanar ) {
+                    mDstSemiPlanar = true;
+                } else if (colorFormat == CodecCapabilities.COLOR_FormatYUV420Planar ) {
+                    mDstSemiPlanar = false;
+                } else {
+                    fail("unsupported color format " + Integer.toHexString(colorFormat));
+                }
+            }
+        }
+        long finish = System.currentTimeMillis();
+        codec.stop();
+        codec.release();
+        codec = null;
+        assertTrue(outFrameCount >= TOTAL_FRAMES);
+        // divide by 3 as sum is done for Y, U, V.
+        double errorRms = Math.sqrt(totalErrorSquared / PIXEL_CHECK_PER_FRAME / outFrameCount / 3);
+        double[] result = { (double) finish - start, errorRms };
+        return result;
+    }
+
+    /**
+     *  returns origin in the absolute frame for given frame count.
+     *  The video scene is moving by moving origin per each frame.
+     */
+    private Point getOrigin(int frameCount) {
+        if (frameCount < 100) {
+            return new Point(2 * frameCount, 0);
+        } else if (frameCount < 200) {
+            return new Point(200, (frameCount - 100) * 2);
+        } else {
+            if (frameCount > 300) { // for safety
+                frameCount = 300;
+            }
+            return new Point(600 - frameCount * 2, 600 - frameCount * 2);
+        }
+    }
+
+    /**
+     * initialize reference YUV plane
+     * @param w This should be YUV_PLANE_ADDITIONAL_LENGTH pixels bigger than video resolution
+     *          to allow movements
+     * @param h This should be YUV_PLANE_ADDITIONAL_LENGTH pixels bigger than video resolution
+     *          to allow movements
+     * @param semiPlanarEnc
+     * @param semiPlanarDec
+     */
+    private void initYUVPlane(int w, int h, boolean semiPlanarEnc, boolean semiPlanarDec) {
+        int bufferSizeY = w * h;
+        mYBuffer = ByteBuffer.allocate(bufferSizeY);
+        mUVBuffer = ByteBuffer.allocate(bufferSizeY / 2);
+        mSrcSemiPlanar = semiPlanarEnc;
+        mDstSemiPlanar = semiPlanarDec;
+        mBufferWidth = w;
+        mBufferHeight = h;
+        final byte[] yArray = mYBuffer.array();
+        final byte[] uvArray = mUVBuffer.array();
+        for (int i = 0; i < h; i++) {
+            for (int j = 0; j < w; j++) {
+                yArray[i * w + j]  = clampY((i + j) & 0xff);
+            }
+        }
+        if (semiPlanarEnc) {
+            for (int i = 0; i < h/2; i++) {
+                for (int j = 0; j < w/2; j++) {
+                    uvArray[i * w + 2 * j]  = (byte) (i & 0xff);
+                    uvArray[i * w + 2 * j + 1]  = (byte) (j & 0xff);
+                }
+            }
+        } else { // planar, U first, then V
+            int vOffset = bufferSizeY / 4;
+            for (int i = 0; i < h/2; i++) {
+                for (int j = 0; j < w/2; j++) {
+                    uvArray[i * w/2 + j]  = (byte) (i & 0xff);
+                    uvArray[i * w/2 + vOffset + j]  = (byte) (j & 0xff);
+                }
+            }
+        }
+    }
+
+    /**
+     * class to store pixel values in YUV
+     *
+     */
+    public class YUVValue {
+        public byte mY;
+        public byte mU;
+        public byte mV;
+        public YUVValue() {
+        }
+
+        public boolean equalTo(YUVValue other) {
+            return (mY == other.mY) && (mU == other.mU) && (mV == other.mV);
+        }
+
+        public double calcErrorSquared(YUVValue other) {
+            double yDelta = mY - other.mY;
+            double uDelta = mU - other.mU;
+            double vDelta = mV - other.mV;
+            return yDelta * yDelta + uDelta * uDelta + vDelta * vDelta;
+        }
+    }
+
+    /**
+     * Read YUV values from given position (x,y) for given origin (originX, originY)
+     * The whole data is already available from YBuffer and UVBuffer.
+     * @param result pass the result via this. This is for avoiding creating / destroying too many
+     *               instances
+     */
+    private void getPixelValuesFromYUVBuffers(int originX, int originY, int x, int y,
+            YUVValue result) {
+        result.mY = mYBuffer.get((originY + y) * mBufferWidth + (originX + x));
+        if (mSrcSemiPlanar) {
+            int index = (originY + y) / 2 * mBufferWidth + (originX + x) / 2 * 2;
+            //Log.d(TAG, "YUV " + originX + "," + originY + "," + x + "," + y + "," + index);
+            result.mU = mUVBuffer.get(index);
+            result.mV = mUVBuffer.get(index + 1);
+        } else {
+            int vOffset = mBufferWidth * mBufferHeight / 4;
+            int index = (originY + y) / 2 * mBufferWidth / 2 + (originX + x) / 2;
+            result.mU = mUVBuffer.get(index);
+            result.mV = mUVBuffer.get(vOffset + index);
+        }
+    }
+
+    /**
+     * Read YUV pixels from decoded output buffer for give (x, y) position
+     * Output buffer is composed of Y parts followed by U/V
+     * @param result pass the result via this. This is for avoiding creating / destroying too many
+     *               instances
+     */
+    private void getPixelValuesFromOutputBuffer(ByteBuffer buffer, int x, int y, YUVValue result) {
+        result.mY = buffer.get(y * mVideoWidth + x);
+        if (mDstSemiPlanar) {
+            int index = mVideoWidth * mVideoHeight + y / 2 * mVideoWidth + x / 2 * 2;
+            //Log.d(TAG, "Decoded " + x + "," + y + "," + index);
+            result.mU = buffer.get(index);
+            result.mV = buffer.get(index + 1);
+        } else {
+            int vOffset = mVideoWidth * mVideoHeight / 4;
+            int index = mVideoWidth * mVideoHeight + y / 2 * mVideoWidth / 2 + x / 2;
+            result.mU = buffer.get(index);
+            result.mV = buffer.get(index + vOffset);
+        }
+    }
+
+    /**
+     * Y cannot have full range. clamp it to prevent invalid value.
+     */
+    private byte clampY(int y) {
+        if (y < Y_CLAMP_MIN) {
+            y = Y_CLAMP_MIN;
+        } else if (y > Y_CLAMP_MAX) {
+            y = Y_CLAMP_MAX;
+        }
+        return (byte) (y & 0xff);
+    }
+
+    /**
+     * Search for given codecName and returns CodecCapabilities if found
+     * @param codecName
+     * @param isEncoder true for encoder, false for decoder
+     * @return null if the codec is not supported
+     */
+    private CodecCapabilities getCodecCapability(
+            String codecName, boolean isEncoder) {
+        int codecCount = MediaCodecList.getCodecCount();
+        for (int i = 0; i < codecCount; ++i) {
+            MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
+            String[] types = info.getSupportedTypes();
+            if (isEncoder != info.isEncoder()) {
+                continue;
+            }
+            for (int j = 0; j < types.length; ++j) {
+                if (types[j].compareTo(codecName) == 0) {
+                    CodecCapabilities cap = info.getCapabilitiesForType(types[j]);
+                    Log.i(TAG, "Use codec " + info.getName());
+                    return cap;
+                }
+            }
+        }
+        return null;
+    }
+
+    /// for debugging
+    private void printIntArray(String msg, int[] data) {
+        StringBuilder builder = new StringBuilder();
+        builder.append(msg);
+        builder.append(":");
+        for (int e : data) {
+            builder.append(Integer.toHexString(e));
+            builder.append(",");
+        }
+        builder.deleteCharAt(builder.length() - 1);
+        Log.i(TAG, builder.toString());
+    }
+
+    /// for debugging
+    private void printByteArray(String msg, byte[] data, int offset, int len) {
+        StringBuilder builder = new StringBuilder();
+        builder.append(msg);
+        builder.append(":");
+        for (int i = offset; i < offset + len; i++) {
+            builder.append(Integer.toHexString(data[i]));
+            builder.append(",");
+        }
+        builder.deleteCharAt(builder.length() - 1);
+        Log.i(TAG, builder.toString());
+    }
+
+    /// for debugging
+    private void printByteBuffer(String msg, ByteBuffer data, int offset, int len) {
+        StringBuilder builder = new StringBuilder();
+        builder.append(msg);
+        builder.append(":");
+        for (int i = offset; i < offset + len; i++) {
+            builder.append(Integer.toHexString(data.get(i)));
+            builder.append(",");
+        }
+        builder.deleteCharAt(builder.length() - 1);
+        Log.i(TAG, builder.toString());
+    }
+}
diff --git a/suite/pts/expectations/knownfailures.txt b/suite/pts/expectations/knownfailures.txt
deleted file mode 100644
index 0d4f101..0000000
--- a/suite/pts/expectations/knownfailures.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-[
-]
diff --git a/suite/pts/hostTests/bootup/Android.mk b/suite/pts/hostTests/bootup/Android.mk
index 4d779a0..1872284 100644
--- a/suite/pts/hostTests/bootup/Android.mk
+++ b/suite/pts/hostTests/bootup/Android.mk
@@ -24,7 +24,7 @@
 
 LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed-prebuilt ddmlib-prebuilt junit ptscommonutilhost
 
-LOCAL_PTS_TEST_PACKAGE := com.android.pts.bootup
+LOCAL_CTS_TEST_PACKAGE := com.android.pts.bootup
 
-include $(BUILD_PTS_HOST_JAVA_LIBRARY)
+include $(BUILD_CTS_HOST_JAVA_LIBRARY)
 
diff --git a/suite/pts/hostTests/bootup/src/com/android/pts/bootup/BootupTimeTest.java b/suite/pts/hostTests/bootup/src/com/android/pts/bootup/BootupTimeTest.java
index e78289b..dd7b834 100644
--- a/suite/pts/hostTests/bootup/src/com/android/pts/bootup/BootupTimeTest.java
+++ b/suite/pts/hostTests/bootup/src/com/android/pts/bootup/BootupTimeTest.java
@@ -19,6 +19,8 @@
 import android.cts.util.TimeoutReq;
 import com.android.pts.util.MeasureRun;
 import com.android.pts.util.MeasureTime;
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
 import com.android.pts.util.ReportLog;
 import com.android.pts.util.Stat;
 import com.android.pts.util.Stat.StatResult;
@@ -65,9 +67,11 @@
                 rebootDevice();
             }
         });
-        mReport.printArray("time in ms", result, false);
+        mReport.printArray("bootup time", result, ResultType.LOWER_BETTER,
+                ResultUnit.MS);
         StatResult stat = Stat.getStat(result);
-        mReport.printSummary("time in ms", stat.mAverage, stat.mStddev);
+        mReport.printSummary("bootup time", stat.mAverage, ResultType.LOWER_BETTER,
+                ResultUnit.MS);
     }
 
     private void rebootDevice() throws DeviceNotAvailableException {
diff --git a/suite/pts/hostTests/browser/Android.mk b/suite/pts/hostTests/browser/Android.mk
deleted file mode 100644
index 9e45477..0000000
--- a/suite/pts/hostTests/browser/Android.mk
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright (C) 2012 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE := PtsHostBrowser
-
-LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed-prebuilt ddmlib-prebuilt junit ptscommonutilhost ptshostutil
-
-LOCAL_PTS_TEST_PACKAGE := com.android.pts.browser
-
-include $(BUILD_PTS_HOST_JAVA_LIBRARY)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/suite/pts/hostTests/browser/browserlauncher/src/com/android/pts/browser/LaunchBrowserTest.java b/suite/pts/hostTests/browser/browserlauncher/src/com/android/pts/browser/LaunchBrowserTest.java
deleted file mode 100644
index a9e48f3..0000000
--- a/suite/pts/hostTests/browser/browserlauncher/src/com/android/pts/browser/LaunchBrowserTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.pts.browser;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.Uri;
-import android.webkit.cts.CtsTestServer;
-import com.android.pts.util.PtsAndroidTestCase;
-
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Device side code to run browser benchmarking.
- * It launches an activity with URL and wait for broadcast sent from host.
- * It is host's responsibility to send broadcast after parsing browser's result.
- */
-public class LaunchBrowserTest extends PtsAndroidTestCase {
-
-    private static final String OCTANE_START_FILE = "octane/index.html";
-    private static final String HOST_COMPLETION_BROADCAST = "com.android.pts.browser.completion";
-    private static long BROWSER_COMPLETION_TIMEOUT = 10 * 60;
-    private CtsTestServer mWebServer;
-    private HostBroadcastReceiver mReceiver;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mWebServer = new CtsTestServer(getContext());
-        mReceiver = new HostBroadcastReceiver();
-        mReceiver.register(getContext(), HOST_COMPLETION_BROADCAST);
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        mReceiver.unregister(getContext());
-        mWebServer.shutdown();
-        mWebServer = null;
-        super.tearDown();
-    }
-
-    public void testOctane() throws InterruptedException {
-        String url = mWebServer.getAssetUrl(OCTANE_START_FILE) + "?auto=1";
-        Uri uri = Uri.parse(url);
-        Intent intent = new Intent(Intent.ACTION_VIEW, uri);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        getContext().startActivity(intent);
-        mReceiver.waitForBroadcast(BROWSER_COMPLETION_TIMEOUT);
-    }
-
-    class HostBroadcastReceiver extends BroadcastReceiver {
-        private final Semaphore mSemaphore = new Semaphore(0);
-
-        public void register(Context context, String actionName) {
-            IntentFilter filter = new IntentFilter();
-            filter.addAction(actionName);
-            context.registerReceiver(this, filter);
-        }
-
-        public void unregister(Context context) {
-            context.unregisterReceiver(this);
-        }
-
-        public boolean waitForBroadcast(long timeoutInSec) throws InterruptedException {
-            return mSemaphore.tryAcquire(timeoutInSec, TimeUnit.SECONDS);
-        }
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            mSemaphore.release();
-        }
-    }
-}
diff --git a/suite/pts/hostTests/browser/src/com/android/pts/browser/BrowserTest.java b/suite/pts/hostTests/browser/src/com/android/pts/browser/BrowserTest.java
deleted file mode 100644
index b5400ab..0000000
--- a/suite/pts/hostTests/browser/src/com/android/pts/browser/BrowserTest.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.pts.browser;
-
-import android.cts.util.TimeoutReq;
-
-import com.android.cts.tradefed.build.CtsBuildHelper;
-import com.android.ddmlib.Log;
-import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
-import com.android.pts.ptsutil.LogcatLineReceiver;
-import com.android.pts.util.ReportLog;
-import com.android.pts.util.Stat;
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.result.CollectingTestListener;
-import com.android.tradefed.result.TestRunResult;
-import com.android.tradefed.testtype.DeviceTestCase;
-import com.android.tradefed.testtype.IBuildReceiver;
-
-import java.io.File;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Run browser benchmarking.
- * Benchmarking result is printed via logcat (JavaScript console.log method).
- * Corresponding device side test should be running to make browser benchmarking working,
- * And the device test expects HOST_COMPLETION_BROADCAST broadcast from host as device
- * test cannot detect the completion from browser side.
- */
-public class BrowserTest extends DeviceTestCase implements IBuildReceiver {
-    private static final String BROADCAST_CMD = "am broadcast -a com.android.pts.browser.completion";
-    private static final String TAG = "BrowserTest";
-    private static final String CTS_RUNNER = "android.test.InstrumentationCtsTestRunner";
-    private static final String PACKAGE = "com.android.pts.browser";
-    private static final String APK = "PtsDeviceBrowserLauncher.apk";
-    private static final long LOGCAT_TIMEOUT_IN_SEC = 10 * 60L;
-    private static final String LOGCAT_FILTER = " browser:D chromium:D *:S";
-    private static final long REBOOT_WAIT_TIME_IN_MS = 2 * 60 * 1000L;
-
-    private CtsBuildHelper mBuild;
-    private ITestDevice mDevice;
-    private LogcatLineReceiver mReceiver;
-    private ReportLog mReport;
-
-    private volatile boolean mIgnoreLine = true;
-    private double mResult;
-
-    @Override
-    public void setBuild(IBuildInfo buildInfo) {
-        mBuild = CtsBuildHelper.createBuildHelper(buildInfo);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mReport = new ReportLog();
-        mDevice = getDevice();
-        mDevice.uninstallPackage(PACKAGE);
-        File app = mBuild.getTestApp(APK);
-        mDevice.installPackage(app, false);
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-        mDevice.uninstallPackage(PACKAGE);
-        mReport.throwReportToHost();
-    }
-
-    @TimeoutReq(minutes = 40)
-    public void testOctane() throws Exception {
-        String resultPattern = "([A-Z][\\d\\w]+): ([\\d]+)";
-        String summaryPattern = "(Octane Score.*): ([\\d]+)";
-        int numberRepeat = 5;
-        double[] results = new double[numberRepeat];
-        for (int i = 0; i < numberRepeat; i++) {
-            Log.i(TAG, i + "-th round");
-            // browser will not refresh if the page is already loaded.
-            mDevice.reboot();
-            Thread.sleep(REBOOT_WAIT_TIME_IN_MS);
-            results[i] = runBenchmarking("testOctane", resultPattern, summaryPattern);
-        }
-        mReport.printArray("scores", results, true);
-        Stat.StatResult stat = Stat.getStat(results);
-        mReport.printSummary("Score", stat.mAverage, stat.mStddev);
-    }
-
-    private double runBenchmarking(String testMethodName, String resultPattern,
-            String summaryPattern) throws DeviceNotAvailableException, InterruptedException {
-        RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(PACKAGE, CTS_RUNNER,
-                mDevice.getIDevice());
-        testRunner.setMethodName("com.android.pts.browser.LaunchBrowserTest", testMethodName);
-        CollectingTestListener listener = new CollectingTestListener();
-        mIgnoreLine = true;
-        startLogMonitoring(resultPattern, summaryPattern);
-        // hack to ignore already captured logcat as the monitor will get it again.
-        // Checking time and skipping may be a better logic, but simply throwing away also works.
-        Thread.sleep(5000);
-        mIgnoreLine = false;
-        Log.i(TAG, "start to run test in device");
-        mDevice.runInstrumentationTests(testRunner, listener);
-        TestRunResult result = listener.getCurrentRunResults();
-        if (result.isRunFailure()) {
-            fail(result.getRunFailureMessage());
-        }
-        if (result.getNumPassedTests() == 0) {
-            fail("maybe timeout");
-        }
-        stopLogMonitoring();
-        return mResult;
-    }
-
-    void startLogMonitoring(String resultPattern, String summaryPattern)
-            throws InterruptedException, DeviceNotAvailableException {
-        final Pattern result = Pattern.compile(resultPattern);
-        final Pattern summary = Pattern.compile(summaryPattern);
-        mReceiver = new LogcatLineReceiver(mDevice, LOGCAT_FILTER, LOGCAT_TIMEOUT_IN_SEC) {
-            @Override
-            public void processALine(String line) throws DeviceNotAvailableException {
-                Log.i(TAG, "processALine " + line + " ignore " + mIgnoreLine);
-                if (mIgnoreLine) {
-                    return;
-                }
-                Matcher matchResult = result.matcher(line);
-                if (matchResult.find()) {
-                    mReport.printValue(matchResult.group(1),
-                            Double.parseDouble(matchResult.group(2)));
-                }
-                Matcher matchSummary = summary.matcher(line);
-                if (matchSummary.find()) {
-                    mResult = Double.parseDouble(matchSummary.group(2));
-                    mReport.printValue(matchSummary.group(1), mResult);
-                    mDevice.executeShellCommand(BROADCAST_CMD);
-                }
-            }
-        };
-        mReceiver.start();
-    }
-
-    void stopLogMonitoring() {
-        mReceiver.stop();
-    }
-}
diff --git a/suite/pts/hostTests/uihost/Android.mk b/suite/pts/hostTests/uihost/Android.mk
index 77673a2..65742e2 100644
--- a/suite/pts/hostTests/uihost/Android.mk
+++ b/suite/pts/hostTests/uihost/Android.mk
@@ -24,8 +24,8 @@
 
 LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed-prebuilt ddmlib-prebuilt junit ptscommonutilhost
 
-LOCAL_PTS_TEST_PACKAGE := com.android.pts.uihost
+LOCAL_CTS_TEST_PACKAGE := com.android.pts.uihost
 
-include $(BUILD_PTS_HOST_JAVA_LIBRARY)
+include $(BUILD_CTS_HOST_JAVA_LIBRARY)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/suite/pts/hostTests/uihost/appA/Android.mk b/suite/pts/hostTests/uihost/appA/Android.mk
index 7dce44c..4d31cb4 100644
--- a/suite/pts/hostTests/uihost/appA/Android.mk
+++ b/suite/pts/hostTests/uihost/appA/Android.mk
@@ -30,6 +30,6 @@
 
 LOCAL_SDK_VERSION := 16
 
-include $(BUILD_PTS_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
 
 
diff --git a/suite/pts/hostTests/uihost/appB/Android.mk b/suite/pts/hostTests/uihost/appB/Android.mk
index caa1090..bb1b5d7 100644
--- a/suite/pts/hostTests/uihost/appB/Android.mk
+++ b/suite/pts/hostTests/uihost/appB/Android.mk
@@ -30,6 +30,6 @@
 
 LOCAL_SDK_VERSION := 16
 
-include $(BUILD_PTS_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
 
 
diff --git a/suite/pts/hostTests/uihost/control/Android.mk b/suite/pts/hostTests/uihost/control/Android.mk
index 86ceb7c..dec5ad1 100644
--- a/suite/pts/hostTests/uihost/control/Android.mk
+++ b/suite/pts/hostTests/uihost/control/Android.mk
@@ -30,4 +30,4 @@
 
 LOCAL_SDK_VERSION := 16
 
-include $(BUILD_PTS_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
diff --git a/suite/pts/hostTests/uihost/control/src/com/android/pts/taskswitching/control/TaskswitchingDeviceTest.java b/suite/pts/hostTests/uihost/control/src/com/android/pts/taskswitching/control/TaskswitchingDeviceTest.java
index fc5bc04..5147eaf 100644
--- a/suite/pts/hostTests/uihost/control/src/com/android/pts/taskswitching/control/TaskswitchingDeviceTest.java
+++ b/suite/pts/hostTests/uihost/control/src/com/android/pts/taskswitching/control/TaskswitchingDeviceTest.java
@@ -27,6 +27,8 @@
 
 import com.android.pts.util.MeasureRun;
 import com.android.pts.util.MeasureTime;
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
 import com.android.pts.util.PtsAndroidTestCase;
 import com.android.pts.util.Stat;
 
@@ -81,9 +83,11 @@
                 }
             }
         });
-        getReportLog().printArray("ms", results, false);
+        getReportLog().printArray("taskswitching time", results, ResultType.LOWER_BETTER,
+                ResultUnit.MS);
         Stat.StatResult stat = Stat.getStat(results);
-        getReportLog().printSummary("Time ms", stat.mAverage, stat.mStddev);
+        getReportLog().printSummary("taskswitching time", stat.mAverage,
+                ResultType.LOWER_BETTER, ResultUnit.MS);
     }
 
     private void startActivity(String packageName, String activityName) {
diff --git a/suite/pts/hostTests/uihost/src/com/android/pts/uihost/InstallTimeTest.java b/suite/pts/hostTests/uihost/src/com/android/pts/uihost/InstallTimeTest.java
index 4bfcde1..056e0f4 100644
--- a/suite/pts/hostTests/uihost/src/com/android/pts/uihost/InstallTimeTest.java
+++ b/suite/pts/hostTests/uihost/src/com/android/pts/uihost/InstallTimeTest.java
@@ -16,37 +16,26 @@
 
 package com.android.pts.uihost;
 
-import android.cts.util.TimeoutReq;
-
 import com.android.cts.tradefed.build.CtsBuildHelper;
-import com.android.ddmlib.Log;
-import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
-import com.android.ddmlib.testrunner.TestIdentifier;
 import com.android.pts.util.MeasureRun;
 import com.android.pts.util.MeasureTime;
-import com.android.pts.util.PtsException;
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
 import com.android.pts.util.ReportLog;
 import com.android.pts.util.Stat;
 import com.android.pts.util.Stat.StatResult;
 import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.device.TestDeviceOptions;
-import com.android.tradefed.result.CollectingTestListener;
-import com.android.tradefed.result.TestResult;
-import com.android.tradefed.result.TestRunResult;
 import com.android.tradefed.testtype.DeviceTestCase;
 import com.android.tradefed.testtype.IBuildReceiver;
 
 import java.io.File;
-import java.util.Map;
+
 
 /**
  * Test to measure installation time of a APK.
  */
 public class InstallTimeTest extends DeviceTestCase implements IBuildReceiver {
-    private static final String TAG = "InstallTimeTest";
-    private final static String CTS_RUNNER = "android.test.InstrumentationCtsTestRunner";
     private CtsBuildHelper mBuild;
     private ITestDevice mDevice;
     private ReportLog mReport = null;
@@ -89,9 +78,11 @@
                 device.installPackage(app, false);
             }
         });
-        mReport.printArray("time in ms", result, false);
+        mReport.printArray("install time", result, ResultType.LOWER_BETTER,
+                ResultUnit.MS);
         StatResult stat = Stat.getStat(result);
-        mReport.printSummary("time in ms", stat.mAverage, stat.mStddev);
+        mReport.printSummary("install time", stat.mAverage, ResultType.LOWER_BETTER,
+                ResultUnit.MS);
     }
 
 }
diff --git a/suite/pts/lib/commonutil/Android.mk b/suite/pts/lib/commonutil/Android.mk
index 27e0ba7..152f862 100644
--- a/suite/pts/lib/commonutil/Android.mk
+++ b/suite/pts/lib/commonutil/Android.mk
@@ -35,6 +35,8 @@
 
 LOCAL_MODULE_TAGS := optional
 
+LOCAL_JAVA_LIBRARIES := junit
+
 LOCAL_MODULE := ptscommonutilhost
 
 include $(BUILD_HOST_JAVA_LIBRARY)
\ No newline at end of file
diff --git a/suite/pts/lib/commonutil/src/com/android/pts/util/ReportLog.java b/suite/pts/lib/commonutil/src/com/android/pts/util/ReportLog.java
index 0d69c2f..8a5e624 100644
--- a/suite/pts/lib/commonutil/src/com/android/pts/util/ReportLog.java
+++ b/suite/pts/lib/commonutil/src/com/android/pts/util/ReportLog.java
@@ -19,17 +19,17 @@
 import java.util.LinkedList;
 import java.util.List;
 
+import junit.framework.Assert;
+
 
 /**
  * Utility class to print performance measurement result back to host.
  * For now, throws know exception with message.
  *
  * Format:
- * LOG_SEPARATOR : separates each log
- * Message = log [LOG_SEPARATOR log]*
- * log for single value = classMethodName:line_number|header|d|value
- * log for array = classMethodName:line_number|header|da|values|
- *                     average average_value min|max value stddev value
+ * Message = summary log SUMMARY_SEPARATOR [LOG_SEPARATOR log]*
+ * summary = message|target|unit|type|value, target can be " " if there is no target set.
+ * log for array = classMethodName:line_number|message|unit|type|space seSummaryparated values
  */
 public class ReportLog {
     private static final String LOG_SEPARATOR = "+++";
@@ -39,42 +39,80 @@
     private List<String> mMessages = new LinkedList<String> ();
     private String mSummary = null;
     protected static int mDepth = 3;
+
     /**
-     * print given value to the report
-     * @param header string to explain the contents. It can be unit for the value.
-     * @param val
+     * print array of values to output log
      */
-    public void printValue(String header, double val) {
-        String message = getClassMethodNames(mDepth, true) + LOG_ELEM_SEPARATOR + header +
-                LOG_ELEM_SEPARATOR + "d" + LOG_ELEM_SEPARATOR + val;
-        mMessages.add(message);
-        printLog(message);
+    public void printArray(String message, double[] values, ResultType type,
+            ResultUnit unit) {
+        doPrintArray(message, values, type, unit);
     }
 
     /**
-     * array version of printValue
-     * @param header
-     * @param val
-     * @param addMin add minimum to the result. If false, add maximum to the result
+     * Print a value to output log
      */
-    public void printArray(String header, double[] val, boolean addMin) {
+    public void printValue(String message, double value, ResultType type,
+            ResultUnit unit) {
+        double[] vals = { value };
+        doPrintArray(message, vals, type, unit);
+    }
+
+    private void doPrintArray(String message, double[] values, ResultType type,
+    ResultUnit unit) {
         StringBuilder builder = new StringBuilder();
-        builder.append(getClassMethodNames(mDepth, true) + LOG_ELEM_SEPARATOR + header +
-                LOG_ELEM_SEPARATOR + "da" + LOG_ELEM_SEPARATOR);
-        for (double v : val) {
+        // note mDepth + 1 as this function will be called by printVaue or printArray
+        // and we need caller of printValue / printArray
+        builder.append(getClassMethodNames(mDepth + 1, true) + LOG_ELEM_SEPARATOR + message +
+                LOG_ELEM_SEPARATOR + type.getXmlString() + LOG_ELEM_SEPARATOR +
+                unit.getXmlString() + LOG_ELEM_SEPARATOR);
+        for (double v : values) {
             builder.append(v);
             builder.append(" ");
         }
-        Stat.StatResult stat = Stat.getStat(val);
-        builder.append(LOG_ELEM_SEPARATOR + "average " + stat.mAverage +
-                (addMin ? (" min " + stat.mMin) : (" max " + stat.mMax)) + " stddev " + stat.mStddev);
         mMessages.add(builder.toString());
         printLog(builder.toString());
     }
 
-    public void printSummary(String header, double average, double stddev) {
-        mSummary = header + LOG_ELEM_SEPARATOR + "average " + average + LOG_ELEM_SEPARATOR +
-                "stddev " + stddev;
+    /**
+     * record the result of benchmarking with performance target.
+     * Depending on the ResultType, the function can fail if the result
+     * does not meet the target. For example, for the type of HIGHER_BETTER,
+     * value of 1.0 with target of 2.0 will fail.
+     *
+     * @param message message to be printed in the final report
+     * @param target target performance for the benchmarking
+     * @param value measured value
+     * @param type
+     * @param unit
+     */
+    public void printSummaryWithTarget(String message, double target, double value,
+            ResultType type, ResultUnit unit) {
+        mSummary = message + LOG_ELEM_SEPARATOR + target + LOG_ELEM_SEPARATOR + type.getXmlString()
+                + LOG_ELEM_SEPARATOR + unit.getXmlString() + LOG_ELEM_SEPARATOR + value;
+        boolean resultOk = true;
+        if (type == ResultType.HIGHER_BETTER) {
+            resultOk = value >= target;
+        } else if (type == ResultType.LOWER_BETTER) {
+            resultOk = value <= target;
+        }
+        if (!resultOk) {
+            Assert.fail("Measured result " + value + " does not meet perf target " + target +
+                    " with type " + type.getXmlString());
+        }
+    }
+
+    /**
+     * For standard report summary without target value.
+     * Note that this function will not fail as there is no target.
+     * @param messsage
+     * @param value
+     * @param type type of the value
+     * @param unit unit of the data
+     */
+    public void printSummary(String message, double value, ResultType type,
+            ResultUnit unit) {
+        mSummary = message + LOG_ELEM_SEPARATOR + " " + LOG_ELEM_SEPARATOR + type.getXmlString() +
+                LOG_ELEM_SEPARATOR + unit.getXmlString() + LOG_ELEM_SEPARATOR + value;
     }
 
     public void throwReportToHost() throws PtsException {
@@ -114,9 +152,6 @@
 
     /**
      * array version of calcRatePerSecArray
-     * @param change
-     * @param timeInMSec
-     * @return
      */
     public static double[] calcRatePerSecArray(double change, double[] timeInMSec) {
         double[] result = new double[timeInMSec.length];
@@ -134,9 +169,6 @@
     /**
      * copy array from src to dst with given offset in dst.
      * dst should be big enough to hold src
-     * @param src
-     * @param dst
-     * @param dstOffset
      */
     public static void copyArray(double[] src, double[] dst, int dstOffset) {
         for (int i = 0; i < src.length; i++) {
@@ -146,8 +178,6 @@
 
     /**
      * get classname.methodname from call stack of the current thread
-     *
-     * @return
      */
     public static String getClassMethodNames() {
         return getClassMethodNames(mDepth, false);
@@ -155,14 +185,13 @@
 
     private static String getClassMethodNames(int depth, boolean addLineNumber) {
         StackTraceElement[] elements = Thread.currentThread().getStackTrace();
-        String names = elements[depth].getClassName() + "." + elements[depth].getMethodName() +
+        String names = elements[depth].getClassName() + "#" + elements[depth].getMethodName() +
                 (addLineNumber ? ":" + elements[depth].getLineNumber() : "");
         return names;
     }
 
     /**
      * to be overridden by child to print message to be passed
-     * @param msg
      */
     protected void printLog(String msg) {
 
diff --git a/suite/pts/lib/commonutil/src/com/android/pts/util/ResultType.java b/suite/pts/lib/commonutil/src/com/android/pts/util/ResultType.java
new file mode 100644
index 0000000..499654d
--- /dev/null
+++ b/suite/pts/lib/commonutil/src/com/android/pts/util/ResultType.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.pts.util;
+
+/**
+ * Enum for distinguishing performance results.
+ */
+public enum ResultType {
+    /** lower score shows better performance */
+    LOWER_BETTER("lowerBetter"),
+    /** higher score shows better performance */
+    HIGHER_BETTER("higherBetter"),
+    /** This value is not directly correlated with performance. */
+    NEUTRAL("neutral"),
+    /** presence of this type requires some attention although it may not be an error. */
+    WARNING("warning");
+
+    final private String mXmlString;
+    ResultType(String xmlString) {
+        mXmlString = xmlString;
+    }
+
+    /**
+     * Return string used in CTS XML report
+     */
+    public String getXmlString() {
+        return mXmlString;
+    }
+}
diff --git a/suite/pts/lib/commonutil/src/com/android/pts/util/ResultUnit.java b/suite/pts/lib/commonutil/src/com/android/pts/util/ResultUnit.java
new file mode 100644
index 0000000..b234cb5
--- /dev/null
+++ b/suite/pts/lib/commonutil/src/com/android/pts/util/ResultUnit.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.pts.util;
+
+/**
+ * Enum for representing the unit of performance results.
+ *
+ */
+public enum ResultUnit {
+    /** for value with no unit */
+    NONE,
+    /** milli-seconds */
+    MS,
+    /** frames per second */
+    FPS,
+    /** operations per second */
+    OPS,
+    /** kilo-bytes-per-second, not bits-per-second */
+    KBPS,
+    /** mega-bytes-per-second */
+    MBPS,
+    /** amount of data, bytes */
+    BYTE,
+    /** tell how many times it did happen. */
+    COUNT,
+    /** unit for benchmarking with generic score. */
+    SCORE;
+
+    /**
+     * Return string used in CTS XML report
+     */
+    public String getXmlString() {
+        return name().toLowerCase();
+    }
+}
+
diff --git a/suite/pts/lib/commonutil/src/com/android/pts/util/Stat.java b/suite/pts/lib/commonutil/src/com/android/pts/util/Stat.java
index aae51ec..d56f59e 100644
--- a/suite/pts/lib/commonutil/src/com/android/pts/util/Stat.java
+++ b/suite/pts/lib/commonutil/src/com/android/pts/util/Stat.java
@@ -22,6 +22,9 @@
  */
 public class Stat {
 
+    /**
+     * Collection of statistical propertirs like average, max, min, and stddev
+     */
     public static class StatResult {
         public double mAverage;
         public double mMin;
@@ -35,6 +38,9 @@
         }
     }
 
+    /**
+     * Calculate statistics properties likes average, min, max, and stddev for the given array
+     */
     public static StatResult getStat(double[] data) {
         double average = data[0];
         double min = data[0];
@@ -57,6 +63,20 @@
         return new StatResult(average, min, max, stddev);
     }
 
+    /**
+     * return the average value of the passed array
+     */
+    public static double getAverage(double[] data) {
+        double sum = data[0];
+        for (int i = 1; i < data.length; i++) {
+            sum += data[i];
+        }
+        return sum / data.length;
+    }
+
+    /**
+     * return the minimum value of the passed array
+     */
     public static double getMin(double[] data) {
         double min = data[0];
         for (int i = 1; i < data.length; i++) {
@@ -67,6 +87,9 @@
         return min;
     }
 
+    /**
+     * return the maximum value of the passed array
+     */
     public static double getMax(double[] data) {
         double max = data[0];
         for (int i = 1; i < data.length; i++) {
diff --git a/suite/pts/tools/tradefed/pts-tradefed b/suite/pts/tools/tradefed/pts-tradefed
deleted file mode 100755
index 901ad8c..0000000
--- a/suite/pts/tools/tradefed/pts-tradefed
+++ /dev/null
@@ -1,86 +0,0 @@
-#!/bin/bash
-
-# Copyright (C) 2011 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# launcher script for cts-tradefed harness for PTS
-# can be used from an Android build environment, or a standalone pts zip
-
-checkFile() {
-    if [ ! -f "$1" ]; then
-        echo "Unable to locate $1"
-        exit
-    fi;
-}
-
-checkPath() {
-    if ! type -P $1 &> /dev/null; then
-        echo "Unable to find $1 in path."
-        exit
-    fi;
-}
-
-checkPath adb
-checkPath java
-
-# check java version
-JAVA_VERSION=$(java -version 2>&1 | head -n 1 | grep '[ "]1\.6[\. "$$]')
-if [ "${JAVA_VERSION}" == "" ]; then
-    echo "Wrong java version. 1.6 is required."
-    exit
-fi
-
-# check debug flag and set up remote debugging
-if [ -n "${TF_DEBUG}" ]; then
-  if [ -z "${TF_DEBUG_PORT}" ]; then
-    TF_DEBUG_PORT=10088
-  fi
-  RDBG_FLAG=-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=${TF_DEBUG_PORT}
-fi
-
-
-# check if in Android build env
-if [ ! -z ${ANDROID_BUILD_TOP} ]; then
-    HOST=`uname`
-    if [ "$HOST" == "Linux" ]; then
-        OS="linux-x86"
-    elif [ "$HOST" == "Darwin" ]; then
-        OS="darwin-x86"
-    else
-        echo "Unrecognized OS"
-        exit
-    fi;
-    PTS_ROOT=${ANDROID_BUILD_TOP}/out/host/${OS}/pts
-    if [ ! -d ${PTS_ROOT} ]; then
-        echo "Could not find $PTS_ROOT in Android build environment. Try 'make pts'"
-        exit
-    fi;
-fi;
-
-if [ -z ${PTS_ROOT} ]; then
-    # assume we're in an extracted pts install
-    PTS_ROOT="$(dirname $0)/../.."
-fi;
-
-JAR_DIR=${PTS_ROOT}/android-pts/tools
-JARS="ddmlib-prebuilt.jar tradefed-prebuilt.jar hosttestlib.jar cts-tradefed.jar ptscommonutilhost.jar ptshostutil.jar"
-
-for JAR in $JARS; do
-    checkFile ${JAR_DIR}/${JAR}
-    JAR_PATH=${JAR_PATH}:${JAR_DIR}/${JAR}
-done
-
-java $RDBG_FLAG \
-  -cp ${JAR_PATH} -DCTS_ROOT=${PTS_ROOT} -DPTS=1 com.android.cts.tradefed.command.CtsConsole "$@"
-
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 590ee36..e9b802a 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -244,6 +244,14 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="android.widget.cts.GridLayoutStubActivity"
+            android:label="GridLayoutStubActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+            </intent-filter>
+        </activity>
+
         <activity android:name="android.widget.cts.LayoutDirectionStubActivity"
             android:label="LayoutDirectionStubActivity">
             <intent-filter>
diff --git a/tests/SignatureTest/Android.mk b/tests/SignatureTest/Android.mk
index cc0d53c..0b51f25 100644
--- a/tests/SignatureTest/Android.mk
+++ b/tests/SignatureTest/Android.mk
@@ -33,7 +33,11 @@
 # To be passed in on command line
 CTS_API_VERSION ?= current
 
+ifeq (current,$(CTS_API_VERSION))
+android_api_description := frameworks/base/api/$(CTS_API_VERSION).txt
+else
 android_api_description := $(SRC_API_DIR)/$(CTS_API_VERSION).txt
+endif
 
 # Can't call local-intermediates-dir directly here because we have to
 # include BUILD_PACAKGE first.  Can't include BUILD_PACKAGE first
@@ -41,75 +45,50 @@
 # hack.
 intermediates.COMMON := $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),,COMMON)
 signature_res_dir := $(intermediates.COMMON)/genres
-LOCAL_RESOURCE_DIR := $(signature_res_dir)
-
-# These shell commands allow us to get around the package.mk check for
-# empty/non-existent resouce dirs (which ours would be).  If it finds
-# an empty/non-existent resource dir, R_file_stamp doesn't contain a
-# target and our stuff never gets copied.
-# TODO: fix package.mk so we can avoid this hack
-fake_resource_check := $(signature_res_dir)/raw/fake_resource_check
-
-$(shell \
- if [ ! -f $(fake_resource_check) ]; then \
-   mkdir -p $(dir $(fake_resource_check)); \
-   touch $(fake_resource_check); \
- fi \
- )
+LOCAL_RESOURCE_DIR := $(signature_res_dir) $(LOCAL_PATH)/res
 
 include $(BUILD_PACKAGE)
 
-copied_res_stamp := $(intermediates.COMMON)/copyres.stamp
 generated_res_stamp := $(intermediates.COMMON)/genres.stamp
-api_ver_path := $(intermediates.COMMON)
-api_ver_file := $(api_ver_path)/api_ver_is_$(CTS_API_VERSION)
+api_ver_file := $(intermediates.COMMON)/api_ver_is_$(CTS_API_VERSION)
 
 # The api_ver_file keeps track of which api version was last built.
 # By only ever having one of these magic files in existance and making
 # sure the generated resources rule depend on it, we can ensure that
 # the proper version of the api resource gets generated.
 $(api_ver_file):
-	$(hide) rm -f $(api_ver_path)/api_ver_is_*
-	$(hide) touch $@
+	$(hide) rm -f $(dir $@)/api_ver_is_* \
+		&& mkdir -p $(dir $@) && touch $@
 
 android_api_xml_description := $(intermediates.COMMON)/api.xml
-$(android_api_xml_description): PRIVATE_INPUT_FILE := $(android_api_description)
-$(android_api_xml_description): $(android_api_description) $(APICHECK)
-	$(hide) echo "Convert api file to xml: $@"
-	$(hide) $(APICHECK_COMMAND) -convert2xml $(PRIVATE_INPUT_FILE) $@
-
-static_res_deps := $(call find-subdir-assets,$(LOCAL_PATH)/res)
-$(copied_res_stamp): PRIVATE_PATH := $(LOCAL_PATH)
-$(copied_res_stamp): PRIVATE_MODULE := $(LOCAL_MODULE)
-$(copied_res_stamp): PRIVATE_RES_DIR := $(signature_res_dir)
-$(copied_res_stamp): FAKE_RESOURCE_DIR := $(dir $(fake_resource_check))
-$(copied_res_stamp): FAKE_RESOURCE_CHECK := $(fake_resource_check)
-$(copied_res_stamp): $(foreach res,$(static_res_deps),$(LOCAL_PATH)/res/${res}) | $(ACP)
-	$(hide) echo "Copy resources: $(PRIVATE_MODULE)"
-	$(hide) rm -f $@
-	$(hide) rm -rf $(PRIVATE_RES_DIR)
-	$(hide) mkdir -p $(PRIVATE_RES_DIR)
-	$(hide) if [ ! -f $(FAKE_RESOURCE_CHECK) ]; \
-	  then mkdir -p $(FAKE_RESOURCE_DIR); \
-	  touch $(FAKE_RESOURCE_CHECK); \
-	fi
-	$(hide) $(ACP) -rd $(PRIVATE_PATH)/res/* $(PRIVATE_RES_DIR)/
-	$(hide) touch $@
+$(android_api_xml_description): $(android_api_description) | $(APICHECK)
+	@ echo "Convert api file to xml: $@"
+	@ mkdir -p $(dir $@)
+	$(hide) $(APICHECK_COMMAND) -convert2xml $< $@
 
 # Split up config/api/1.xml by "package" and save the files as the
 # resource files of SignatureTest.
 $(generated_res_stamp): PRIVATE_PATH := $(LOCAL_PATH)
 $(generated_res_stamp): PRIVATE_MODULE := $(LOCAL_MODULE)
 $(generated_res_stamp): PRIVATE_RES_DIR := $(signature_res_dir)
+$(generated_res_stamp): PRIVATE_API_XML_DESC := $(android_api_xml_description)
 $(generated_res_stamp): $(api_ver_file)
-$(generated_res_stamp): $(copied_res_stamp) $(android_api_xml_description)
-	$(hide) echo "Copy generated resources: $(PRIVATE_MODULE)"
-	$(hide) rm -f $@
+$(generated_res_stamp): $(android_api_xml_description)
+	@ echo "Copy generated resources: $(PRIVATE_MODULE)"
 	$(hide) python cts/tools/utils/android_api_description_splitter.py \
-		$(android_api_xml_description) $(PRIVATE_RES_DIR) package
+		$(PRIVATE_API_XML_DESC) $(PRIVATE_RES_DIR) package
 	$(hide) touch $@
 
-$(R_file_stamp): $(generated_res_stamp) $(copied_res_stamp)
+$(R_file_stamp): $(generated_res_stamp)
+
+# clean up temp vars
+android_api_xml_description :=
+api_ver_file :=
+generated_res_stamp :=
+signature_res_dir :=
+android_api_description :=
+CTS_API_VERSION :=
+
 
 # Use the folloing include to make our test apk.
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/assets/webkit/test_firstPage.html b/tests/assets/webkit/test_firstPage.html
index 4a2f9b0..18a5548 100644
--- a/tests/assets/webkit/test_firstPage.html
+++ b/tests/assets/webkit/test_firstPage.html
@@ -17,6 +17,6 @@
     <title>First page</title>
     <body>
         <h1>First page</h1>
-        <a herf="webView_goBackAndForward_test_secondPage.html">Go to second page</a>
+        <a href="webView_goBackAndForward_test_secondPage.html">Go to second page</a>
     </body>
 </html>
diff --git a/tests/assets/webkit/test_secondPage.html b/tests/assets/webkit/test_secondPage.html
index 952d43f..ea8e2a1 100644
--- a/tests/assets/webkit/test_secondPage.html
+++ b/tests/assets/webkit/test_secondPage.html
@@ -17,6 +17,6 @@
     <title>Second page</title>
     <body>
         <h1>Second page</h1>
-        <a herf="webView_goBackAndForward_test_thirdPage.html">Go to third page</a>
+        <a href="webView_goBackAndForward_test_thirdPage.html">Go to third page</a>
     </body>
 </html>
diff --git a/tests/src/android/view/inputmethod/cts/InputMethodInfoStub.java b/tests/res/layout/gridlayout_layout.xml
similarity index 63%
copy from tests/src/android/view/inputmethod/cts/InputMethodInfoStub.java
copy to tests/res/layout/gridlayout_layout.xml
index 0eeefbe..54b3b2c 100644
--- a/tests/src/android/view/inputmethod/cts/InputMethodInfoStub.java
+++ b/tests/res/layout/gridlayout_layout.xml
@@ -1,5 +1,6 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2012 The Android Open Source Project.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -12,12 +13,11 @@
  * 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.view.inputmethod.cts;
-
-import android.inputmethodservice.InputMethodService;
-
-public class InputMethodInfoStub extends InputMethodService {
-
-}
+ -->
+<GridLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    >
+</GridLayout>
diff --git a/tests/res/layout/viewtreeobserver_layout.xml b/tests/res/layout/viewtreeobserver_layout.xml
index 461a4bc..66e98b2 100644
--- a/tests/res/layout/viewtreeobserver_layout.xml
+++ b/tests/res/layout/viewtreeobserver_layout.xml
@@ -20,17 +20,19 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content">
 
-    <ListView android:id="@+id/listview1"
+    <View android:id="@+id/view1"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"/>
+        android:layout_height="10dip"
+        android:focusable="true"/>
 
-    <ListView android:id="@+id/listview2"
+    <View android:id="@+id/view2"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"/>
+        android:layout_height="10dip"
+        android:focusable="true"/>
 
     <Button android:id="@+id/button1"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content" />
+        android:layout_width="wrap_content"
+        android:layout_height="10dip" />
 
 </LinearLayout>
 
diff --git a/tests/res/raw/set_target_api_11.bc b/tests/res/raw/set_target_api_11.bc
index babbd9e..fa04be6 100644
--- a/tests/res/raw/set_target_api_11.bc
+++ b/tests/res/raw/set_target_api_11.bc
Binary files differ
diff --git a/tests/res/raw/set_target_api_12.bc b/tests/res/raw/set_target_api_12.bc
index 92ee8de..abe314f 100644
--- a/tests/res/raw/set_target_api_12.bc
+++ b/tests/res/raw/set_target_api_12.bc
Binary files differ
diff --git a/tests/res/raw/set_target_api_13.bc b/tests/res/raw/set_target_api_13.bc
index c842b98..4fa5b75 100644
--- a/tests/res/raw/set_target_api_13.bc
+++ b/tests/res/raw/set_target_api_13.bc
Binary files differ
diff --git a/tests/res/raw/set_target_api_14.bc b/tests/res/raw/set_target_api_14.bc
index 24d2481..876b37c 100644
--- a/tests/res/raw/set_target_api_14.bc
+++ b/tests/res/raw/set_target_api_14.bc
Binary files differ
diff --git a/tests/res/raw/set_target_api_15.bc b/tests/res/raw/set_target_api_15.bc
index a6e86d4..d2de6d9 100644
--- a/tests/res/raw/set_target_api_15.bc
+++ b/tests/res/raw/set_target_api_15.bc
Binary files differ
diff --git a/tests/res/raw/set_target_api_16.bc b/tests/res/raw/set_target_api_16.bc
index 5ded6fd..ca9aa65 100644
--- a/tests/res/raw/set_target_api_16.bc
+++ b/tests/res/raw/set_target_api_16.bc
Binary files differ
diff --git a/tests/res/raw/set_target_api_17.bc b/tests/res/raw/set_target_api_17.bc
new file mode 100644
index 0000000..b08b389
--- /dev/null
+++ b/tests/res/raw/set_target_api_17.bc
Binary files differ
diff --git a/tests/res/raw/set_target_api_too_high.bc b/tests/res/raw/set_target_api_too_high.bc
new file mode 100644
index 0000000..95b2026
--- /dev/null
+++ b/tests/res/raw/set_target_api_too_high.bc
Binary files differ
diff --git a/tests/src/android/provider/cts/MediaStoreAudioTestHelper.java b/tests/src/android/provider/cts/MediaStoreAudioTestHelper.java
index 7bcd0c7..a87cd3a 100644
--- a/tests/src/android/provider/cts/MediaStoreAudioTestHelper.java
+++ b/tests/src/android/provider/cts/MediaStoreAudioTestHelper.java
@@ -221,6 +221,60 @@
         }
     }
 
+    public static class Audio3 extends Audio1 {
+        private Audio3() {
+        }
+
+        private static Audio3 sInstance = new Audio3();
+
+        public static Audio3 getInstance() {
+            return sInstance;
+        }
+
+        @Override
+        public ContentValues getContentValues(boolean isInternal) {
+            ContentValues values = super.getContentValues(isInternal);
+            values.put(Media.DATA, values.getAsString(Media.DATA) + "_3");
+            return values;
+        }
+    }
+
+    public static class Audio4 extends Audio1 {
+        private Audio4() {
+        }
+
+        private static Audio4 sInstance = new Audio4();
+
+        public static Audio4 getInstance() {
+            return sInstance;
+        }
+
+        @Override
+        public ContentValues getContentValues(boolean isInternal) {
+            ContentValues values = super.getContentValues(isInternal);
+            values.put(Media.DATA, values.getAsString(Media.DATA) + "_4");
+            return values;
+        }
+    }
+
+    public static class Audio5 extends Audio1 {
+        private Audio5() {
+        }
+
+        private static Audio5 sInstance = new Audio5();
+
+        public static Audio5 getInstance() {
+            return sInstance;
+        }
+
+        @Override
+        public ContentValues getContentValues(boolean isInternal) {
+            ContentValues values = super.getContentValues(isInternal);
+            values.put(Media.DATA, values.getAsString(Media.DATA) + "_5");
+            return values;
+        }
+    }
+
     // These constants are not part of the public API
     public static final String EXTERNAL_VOLUME_NAME = "external";
     public static final String INTERNAL_VOLUME_NAME = "internal";
diff --git a/tests/src/android/renderscript/cts/setelementat.rs b/tests/src/android/renderscript/cts/setelementat.rs
new file mode 100644
index 0000000..75e96f5
--- /dev/null
+++ b/tests/src/android/renderscript/cts/setelementat.rs
@@ -0,0 +1,51 @@
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+#include "shared.rsh"
+
+int memset_toValue = 0;
+
+int compare_value = 0;
+int compare_failure = 2;
+int failure_value = 0;
+
+uint32_t dimX = 0;
+uint32_t dimY = 0;
+rs_allocation array;
+
+void memset(int *aout) {
+    *aout = memset_toValue;
+    return;
+}
+
+void compare(const int *ain) {
+    if (*ain != compare_value) {
+        rsAtomicCas(&compare_failure, 2, -1);
+        failure_value = *ain;
+    }
+    return;
+}
+
+void getCompareResult(int* aout) {
+    *aout = compare_failure;
+}
+
+void setLargeArray(const int *ain, uint32_t x) {
+    int source = 10;
+    if (x == 0) {
+        for (uint32_t i = 0; i < dimX; i++) {
+            rsSetElementAt(array, &source, i);
+        }
+    }
+}
+
+void setLargeArray2D(const int *ain, uint32_t x) {
+    int source = 10;
+    if (x == 0) {
+        for (uint32_t y = 0; y < dimY; y++) {
+            for (uint32_t xtemp = 0; xtemp < dimX; xtemp++) {
+                rsSetElementAt(array, &source, xtemp, y);
+            }
+        }
+    }
+}
diff --git a/tests/src/android/view/inputmethod/cts/InputMethodInfoStub.java b/tests/src/android/view/inputmethod/cts/InputMethodSettingsActivityStub.java
similarity index 84%
rename from tests/src/android/view/inputmethod/cts/InputMethodInfoStub.java
rename to tests/src/android/view/inputmethod/cts/InputMethodSettingsActivityStub.java
index 0eeefbe..58aa364 100644
--- a/tests/src/android/view/inputmethod/cts/InputMethodInfoStub.java
+++ b/tests/src/android/view/inputmethod/cts/InputMethodSettingsActivityStub.java
@@ -16,8 +16,8 @@
 
 package android.view.inputmethod.cts;
 
-import android.inputmethodservice.InputMethodService;
+import android.preference.PreferenceActivity;
 
-public class InputMethodInfoStub extends InputMethodService {
+public class InputMethodSettingsActivityStub extends PreferenceActivity {
 
 }
diff --git a/tests/src/android/widget/cts/GridLayoutStubActivity.java b/tests/src/android/widget/cts/GridLayoutStubActivity.java
new file mode 100644
index 0000000..3fa0f28
--- /dev/null
+++ b/tests/src/android/widget/cts/GridLayoutStubActivity.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget.cts;
+
+import android.app.Activity;
+import android.os.Bundle;
+import com.android.cts.stub.R;
+
+/**
+ * A minimal application for {@link android.widget.GridLayout} test.
+ */
+public class GridLayoutStubActivity extends Activity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.gridlayout_layout);
+    }
+}
diff --git a/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java b/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java
index 9b46a9b..58b32d6 100644
--- a/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java
+++ b/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java
@@ -31,7 +31,7 @@
 public class AccessibilityEventTest extends TestCase {
 
     /** The number of properties of the {@link AccessibilityEvent} class. */
-    private static final int NON_STATIC_FIELD_COUNT = 30;
+    private static final int NON_STATIC_FIELD_COUNT = 28;
 
     /**
      * Test that no new fields have been added without updating the
@@ -253,7 +253,6 @@
                 ((Message) expectedEvent.getParcelableData()).what,
                 ((Message) receivedEvent.getParcelableData()).what);
 
-        AccessibilityRecord expectedRecord = expectedEvent.getRecord(0);
         AccessibilityRecord receivedRecord = receivedEvent.getRecord(0);
         AccessibilityRecordTest.assertEqualAccessibilityRecord(expectedEvent, receivedRecord);
     }
diff --git a/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java b/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
index e0ffaf7..f26e83c 100644
--- a/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
+++ b/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
@@ -30,7 +30,7 @@
 public class AccessibilityNodeInfoTest extends AndroidTestCase {
 
     /** The number of properties of the {@link AccessibilityNodeInfo} class. */
-    private static final int NON_STATIC_FIELD_COUNT = 19;
+    private static final int NON_STATIC_FIELD_COUNT = 18;
 
     @SmallTest
     public void testMarshaling() throws Exception {
@@ -124,6 +124,7 @@
         info.setMovementGranularities(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
         info.setLabeledBy(new View(getContext()));
         info.setLabelFor(new View(getContext()));
+        info.setViewId("foo.bar:id/baz");
     }
 
     /**
@@ -180,6 +181,8 @@
         assertSame("movementGranularities has incorrect value",
                 expectedInfo.getMovementGranularities(),
                 receivedInfo.getMovementGranularities());
+        assertEquals("viewId has incorrect value", expectedInfo.getViewId(),
+                receivedInfo.getViewId());
     }
 
     /**
@@ -211,5 +214,6 @@
         assertFalse("accessibilityFocused not properly recycled", info.isAccessibilityFocused());
         assertSame("movementGranularities not properly recycled", 0,
                 info.getMovementGranularities());
+        assertNull("viewId not properly recycled", info.getViewId());
     }
 }
diff --git a/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextTraversalTest.java b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextTraversalTest.java
index 517b71a..a508452 100644
--- a/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextTraversalTest.java
+++ b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextTraversalTest.java
@@ -17,6 +17,7 @@
 import android.os.Bundle;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.text.Selection;
+import android.text.TextUtils;
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -86,7 +87,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(View.class.getName())
                         && event.getContentDescription().toString().equals(
@@ -94,7 +95,7 @@
                         && event.getFromIndex() == 0
                         && event.getToIndex() == 1
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -116,7 +117,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(View.class.getName())
                         && event.getContentDescription().toString().equals(
@@ -124,7 +125,7 @@
                         && event.getFromIndex() == 1
                         && event.getToIndex() == 2
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -146,7 +147,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(View.class.getName())
                         && event.getContentDescription().toString().equals(
@@ -154,7 +155,7 @@
                         && event.getFromIndex() == 2
                         && event.getToIndex() == 3
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+                                 AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -165,7 +166,37 @@
         assertFalse(getInteractionBridge().performAction(text,
                 AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments));
 
-        // Move to the next character and wait for an event.
+        // Move to the previous character and wait for an event.
+        AccessibilityEvent fourthExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(View.class.getName())
+                        && event.getContentDescription().toString().equals(
+                                getString(R.string.a_b))
+                        && event.getFromIndex() == 2
+                        && event.getToIndex() == 3
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(fourthExpected);
+
+        // Move to the previous character and wait for an event.
         AccessibilityEvent fifthExpected = getInteractionBridge()
                 .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
             @Override
@@ -180,7 +211,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(View.class.getName())
                         && event.getContentDescription().toString().equals(
@@ -188,7 +219,7 @@
                         && event.getFromIndex() == 1
                         && event.getToIndex() == 2
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -210,7 +241,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(View.class.getName())
                         && event.getContentDescription().toString().equals(
@@ -218,7 +249,7 @@
                         && event.getFromIndex() == 0
                         && event.getToIndex() == 1
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -269,7 +300,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(View.class.getName())
                         && event.getContentDescription().toString().equals(
@@ -277,7 +308,7 @@
                         && event.getFromIndex() == 0
                         && event.getToIndex() == 3
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -299,7 +330,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(View.class.getName())
                         && event.getContentDescription().toString().equals(
@@ -307,7 +338,7 @@
                         && event.getFromIndex() == 4
                         && event.getToIndex() == 7
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -329,7 +360,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(View.class.getName())
                         && event.getContentDescription().toString().equals(
@@ -337,7 +368,7 @@
                         && event.getFromIndex() == 8
                         && event.getToIndex() == 11
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+                               AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -363,7 +394,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(View.class.getName())
                         && event.getContentDescription().toString().equals(
@@ -371,7 +402,7 @@
                         && event.getFromIndex() == 8
                         && event.getToIndex() == 11
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -393,7 +424,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(View.class.getName())
                         && event.getContentDescription().toString().equals(
@@ -401,7 +432,7 @@
                         && event.getFromIndex() == 4
                         && event.getToIndex() == 7
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -423,7 +454,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(View.class.getName())
                         && event.getContentDescription().toString().equals(
@@ -431,7 +462,7 @@
                         && event.getFromIndex() == 0
                         && event.getToIndex() == 3
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -484,7 +515,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(TextView.class.getName())
                         && event.getText().size() > 0
@@ -492,7 +523,7 @@
                         && event.getFromIndex() == 0
                         && event.getToIndex() == 1
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -518,7 +549,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(TextView.class.getName())
                         && event.getText().size() > 0
@@ -526,7 +557,7 @@
                         && event.getFromIndex() == 1
                         && event.getToIndex() == 2
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -552,7 +583,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(TextView.class.getName())
                         && event.getText().size() > 0
@@ -560,7 +591,7 @@
                         && event.getFromIndex() == 2
                         && event.getToIndex() == 3
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -579,7 +610,7 @@
         assertEquals(3, Selection.getSelectionStart(textView.getText()));
         assertEquals(3, Selection.getSelectionEnd(textView.getText()));
 
-        // Move to the next character and wait for an event.
+        // Move to the previous character and wait for an event.
         AccessibilityEvent fifthExpected = getInteractionBridge()
                 .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
             @Override
@@ -594,15 +625,15 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(TextView.class.getName())
                         && event.getText().size() > 0
                         && event.getText().get(0).toString().equals(getString(R.string.a_b))
-                        && event.getFromIndex() == 1
-                        && event.getToIndex() == 2
+                        && event.getFromIndex() == 2
+                        && event.getToIndex() == 3
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -613,7 +644,7 @@
         assertEquals(2, Selection.getSelectionStart(textView.getText()));
         assertEquals(2, Selection.getSelectionEnd(textView.getText()));
 
-        // Move to the next character and wait for an event.
+        // Move to the previous character and wait for an event.
         AccessibilityEvent sixthExpected = getInteractionBridge()
                 .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
             @Override
@@ -628,15 +659,15 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(TextView.class.getName())
                         && event.getText().size() > 0
                         && event.getText().get(0).toString().equals(getString(R.string.a_b))
-                        && event.getFromIndex() == 0
-                        && event.getToIndex() == 1
+                        && event.getFromIndex() == 1
+                        && event.getToIndex() == 2
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -647,6 +678,40 @@
         assertEquals(1, Selection.getSelectionStart(textView.getText()));
         assertEquals(1, Selection.getSelectionEnd(textView.getText()));
 
+        // Move to the previous character and wait for an event.
+        AccessibilityEvent seventhExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(TextView.class.getName())
+                        && event.getText().size() > 0
+                        && event.getText().get(0).toString().equals(getString(R.string.a_b))
+                        && event.getFromIndex() == 0
+                        && event.getToIndex() == 1
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(seventhExpected);
+
+        // Verify the selection position.
+        assertEquals(0, Selection.getSelectionStart(textView.getText()));
+        assertEquals(0, Selection.getSelectionEnd(textView.getText()));
+
         // Make sure there is no previous.
         assertFalse(getInteractionBridge().performAction(text,
                 AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments));
@@ -657,6 +722,284 @@
     }
 
     @MediumTest
+    public void testActionNextAndPreviousAtGranularityCharacterOverTextExtend()
+            throws Exception {
+        final EditText editText = (EditText) getActivity().findViewById(R.id.edit);
+
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                editText.setText(getString(R.string.a_b));
+                Selection.removeSelection(editText.getText());
+            }
+        });
+
+        final AccessibilityNodeInfo text = getInteractionBridge()
+               .findAccessibilityNodeInfoByTextFromRoot(getString(R.string.a_b));
+
+        final int granularities = text.getMovementGranularities();
+        assertEquals(granularities, AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER
+                | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD
+                | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE
+                | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
+                | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+
+        final Bundle arguments = new Bundle();
+        arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
+                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+        arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, true);
+
+        // Move to the next character and wait for an event.
+        AccessibilityEvent firstExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(EditText.class.getName())
+                        && event.getText().size() > 0
+                        && event.getText().get(0).toString().equals(getString(R.string.a_b))
+                        && event.getFromIndex() == 0
+                        && event.getToIndex() == 1
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(firstExpected);
+
+        // Verify the selection position.
+        assertEquals(0, Selection.getSelectionStart(editText.getText()));
+        assertEquals(1, Selection.getSelectionEnd(editText.getText()));
+
+        // Move to the next character and wait for an event.
+        AccessibilityEvent secondExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(EditText.class.getName())
+                        && event.getText().size() > 0
+                        && event.getText().get(0).toString().equals(getString(R.string.a_b))
+                        && event.getFromIndex() == 1
+                        && event.getToIndex() == 2
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(secondExpected);
+
+        // Verify the selection position.
+        assertEquals(0, Selection.getSelectionStart(editText.getText()));
+        assertEquals(2, Selection.getSelectionEnd(editText.getText()));
+
+        // Move to the next character and wait for an event.
+        AccessibilityEvent thirdExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(EditText.class.getName())
+                        && event.getText().size() > 0
+                        && event.getText().get(0).toString().equals(getString(R.string.a_b))
+                        && event.getFromIndex() == 2
+                        && event.getToIndex() == 3
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(thirdExpected);
+
+        // Verify the selection position.
+        assertEquals(0, Selection.getSelectionStart(editText.getText()));
+        assertEquals(3, Selection.getSelectionEnd(editText.getText()));
+
+        // Make sure there is no next.
+        assertFalse(getInteractionBridge().performAction(text,
+                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments));
+
+        // Verify the selection position.
+        assertEquals(0, Selection.getSelectionStart(editText.getText()));
+        assertEquals(3, Selection.getSelectionEnd(editText.getText()));
+
+        // Focus the view so we can change selection.
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                editText.setFocusable(true);
+                editText.requestFocus();
+            }
+        });
+
+        // Put selection at the end of the text.
+        Bundle setSelectionArgs = new Bundle();
+        setSelectionArgs.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 3);
+        setSelectionArgs.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, 3);
+        assertTrue(getInteractionBridge().performAction(text,
+                AccessibilityNodeInfo.ACTION_SET_SELECTION, setSelectionArgs));
+
+        // Verify the selection position.
+        assertEquals(3, Selection.getSelectionStart(editText.getText()));
+        assertEquals(3, Selection.getSelectionEnd(editText.getText()));
+
+        // Unfocus the view so we can get rid of the soft-keyboard.
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                editText.clearFocus();
+                editText.setFocusable(false);
+            }
+        });
+
+        // Move to the previous character and wait for an event.
+        AccessibilityEvent fifthExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(EditText.class.getName())
+                        && event.getText().size() > 0
+                        && event.getText().get(0).toString().equals(getString(R.string.a_b))
+                        && event.getFromIndex() == 2
+                        && event.getToIndex() == 3
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(fifthExpected);
+
+        // Verify the selection position.
+        assertEquals(2, Selection.getSelectionStart(editText.getText()));
+        assertEquals(3, Selection.getSelectionEnd(editText.getText()));
+
+        // Move to the previous character and wait for an event.
+        AccessibilityEvent sixthExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(EditText.class.getName())
+                        && event.getText().size() > 0
+                        && event.getText().get(0).toString().equals(getString(R.string.a_b))
+                        && event.getFromIndex() == 1
+                        && event.getToIndex() == 2
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(sixthExpected);
+
+        // Verify the selection position.
+        assertEquals(1, Selection.getSelectionStart(editText.getText()));
+        assertEquals(3, Selection.getSelectionEnd(editText.getText()));
+
+        // Move to the previous character and wait for an event.
+        AccessibilityEvent seventhExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(EditText.class.getName())
+                        && event.getText().size() > 0
+                        && event.getText().get(0).toString().equals(getString(R.string.a_b))
+                        && event.getFromIndex() == 0
+                        && event.getToIndex() == 1
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(seventhExpected);
+
+        // Verify the selection position.
+        assertEquals(0, Selection.getSelectionStart(editText.getText()));
+        assertEquals(3, Selection.getSelectionEnd(editText.getText()));
+
+        // Make sure there is no previous.
+        assertFalse(getInteractionBridge().performAction(text,
+                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments));
+
+        // Verify the selection position.
+        assertEquals(0, Selection.getSelectionStart(editText.getText()));
+        assertEquals(3, Selection.getSelectionEnd(editText.getText()));
+    }
+
+    @MediumTest
     public void testActionNextAndPreviousAtGranularityWordOverText() throws Exception {
         final TextView textView = (TextView) getActivity().findViewById(R.id.text);
 
@@ -696,7 +1039,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(TextView.class.getName())
                         && event.getText().size() > 0
@@ -704,7 +1047,7 @@
                         && event.getFromIndex() == 0
                         && event.getToIndex() == 3
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -730,7 +1073,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(TextView.class.getName())
                         && event.getText().size() > 0
@@ -738,7 +1081,7 @@
                         && event.getFromIndex() == 4
                         && event.getToIndex() == 7
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -764,7 +1107,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(TextView.class.getName())
                         && event.getText().size() > 0
@@ -772,7 +1115,7 @@
                         && event.getFromIndex() == 8
                         && event.getToIndex() == 11
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+                                 AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -806,7 +1149,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(TextView.class.getName())
                         && event.getText().size() > 0
@@ -814,7 +1157,7 @@
                         && event.getFromIndex() == 8
                         && event.getToIndex() == 11
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -840,7 +1183,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(TextView.class.getName())
                         && event.getText().size() > 0
@@ -848,7 +1191,7 @@
                         && event.getFromIndex() == 4
                         && event.getToIndex() == 7
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -874,7 +1217,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(TextView.class.getName())
                         && event.getText().size() > 0
@@ -882,7 +1225,7 @@
                         && event.getFromIndex() == 0
                         && event.getToIndex() == 3
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -903,6 +1246,279 @@
     }
 
     @MediumTest
+    public void testActionNextAndPreviousAtGranularityWordOverTextExtend() throws Exception {
+        final EditText editText = (EditText) getActivity().findViewById(R.id.edit);
+
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                editText.setText(getString(R.string.foo_bar_baz));
+                Selection.removeSelection(editText.getText());
+            }
+        });
+
+        final AccessibilityNodeInfo text = getInteractionBridge()
+               .findAccessibilityNodeInfoByTextFromRoot(getString(R.string.foo_bar_baz));
+
+        final int granularities = text.getMovementGranularities();
+        assertEquals(granularities, AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER
+                | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD
+                | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE
+                | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
+                | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+
+        final Bundle arguments = new Bundle();
+        arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
+                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+        arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, true);
+
+        // Move to the next word and wait for an event.
+        AccessibilityEvent firstExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(EditText.class.getName())
+                        && event.getText().size() > 0
+                        && event.getText().get(0).toString().equals(getString(R.string.foo_bar_baz))
+                        && event.getFromIndex() == 0
+                        && event.getToIndex() == 3
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(firstExpected);
+
+        // Verify the selection position.
+        assertEquals(0, Selection.getSelectionStart(editText.getText()));
+        assertEquals(3, Selection.getSelectionEnd(editText.getText()));
+
+        // Move to the next word and wait for an event.
+        AccessibilityEvent secondExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(EditText.class.getName())
+                        && event.getText().size() > 0
+                        && event.getText().get(0).toString().equals(getString(R.string.foo_bar_baz))
+                        && event.getFromIndex() == 4
+                        && event.getToIndex() == 7
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(secondExpected);
+
+        // Verify the selection position.
+        assertEquals(0, Selection.getSelectionStart(editText.getText()));
+        assertEquals(7, Selection.getSelectionEnd(editText.getText()));
+
+        // Move to the next word and wait for an event.
+        AccessibilityEvent thirdExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(EditText.class.getName())
+                        && event.getText().size() > 0
+                        && event.getText().get(0).toString().equals(getString(R.string.foo_bar_baz))
+                        && event.getFromIndex() == 8
+                        && event.getToIndex() == 11
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(thirdExpected);
+
+        // Verify the selection position.
+        assertEquals(0, Selection.getSelectionStart(editText.getText()));
+        assertEquals(11, Selection.getSelectionEnd(editText.getText()));
+
+        // Make sure there is no next.
+        assertFalse(getInteractionBridge().performAction(text,
+                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments));
+
+        // Focus the view so we can change selection.
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                editText.setFocusable(true);
+                editText.requestFocus();
+            }
+        });
+
+        // Put selection at the end of the text.
+        Bundle setSelectionArgs = new Bundle();
+        setSelectionArgs.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 11);
+        setSelectionArgs.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, 11);
+        assertTrue(getInteractionBridge().performAction(text,
+                AccessibilityNodeInfo.ACTION_SET_SELECTION, setSelectionArgs));
+
+        // Verify the selection position.
+        assertEquals(11, Selection.getSelectionStart(editText.getText()));
+        assertEquals(11, Selection.getSelectionEnd(editText.getText()));
+
+        // Unfocus the view so we can get rid of the soft-keyboard.
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                editText.clearFocus();
+                editText.setFocusable(false);
+            }
+        });
+
+        // Move to the next word and wait for an event.
+        AccessibilityEvent fourthExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(EditText.class.getName())
+                        && event.getText().size() > 0
+                        && event.getText().get(0).toString().equals(getString(R.string.foo_bar_baz))
+                        && event.getFromIndex() == 8
+                        && event.getToIndex() == 11
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(fourthExpected);
+
+        // Verify the selection position.
+        assertEquals(8, Selection.getSelectionStart(editText.getText()));
+        assertEquals(11, Selection.getSelectionEnd(editText.getText()));
+
+        // Move to the next word and wait for an event.
+        AccessibilityEvent fifthExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(EditText.class.getName())
+                        && event.getText().size() > 0
+                        && event.getText().get(0).toString().equals(getString(R.string.foo_bar_baz))
+                        && event.getFromIndex() == 4
+                        && event.getToIndex() == 7
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(fifthExpected);
+
+        // Verify the selection position.
+        assertEquals(4, Selection.getSelectionStart(editText.getText()));
+        assertEquals(11, Selection.getSelectionEnd(editText.getText()));
+
+        // Move to the next character and wait for an event.
+        AccessibilityEvent sixthExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(EditText.class.getName())
+                        && event.getText().size() > 0
+                        && event.getText().get(0).toString().equals(getString(R.string.foo_bar_baz))
+                        && event.getFromIndex() == 0
+                        && event.getToIndex() == 3
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(sixthExpected);
+
+        // Verify the selection position.
+        assertEquals(0, Selection.getSelectionStart(editText.getText()));
+        assertEquals(11, Selection.getSelectionEnd(editText.getText()));
+
+        // Make sure there is no previous.
+        assertFalse(getInteractionBridge().performAction(text,
+                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments));
+
+        // Verify the selection position.
+        assertEquals(0, Selection.getSelectionStart(editText.getText()));
+        assertEquals(11, Selection.getSelectionEnd(editText.getText()));
+    }
+
+    @MediumTest
     public void testActionNextAndPreviousAtGranularityLineOverText() throws Exception {
         final TextView textView = (TextView) getActivity().findViewById(R.id.text);
 
@@ -942,7 +1558,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(TextView.class.getName())
                         && event.getText().size() > 0
@@ -951,7 +1567,7 @@
                         && event.getFromIndex() == 0
                         && event.getToIndex() == 25
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -977,7 +1593,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(TextView.class.getName())
                         && event.getText().size() > 0
@@ -986,7 +1602,7 @@
                         && event.getFromIndex() == 25
                         && event.getToIndex() == 53
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
+                                 AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -1012,7 +1628,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(TextView.class.getName())
                         && event.getText().size() > 0
@@ -1021,7 +1637,7 @@
                         && event.getFromIndex() == 53
                         && event.getToIndex() == 60
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -1055,7 +1671,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(TextView.class.getName())
                         && event.getText().size() > 0
@@ -1064,7 +1680,7 @@
                         && event.getFromIndex() == 53
                         && event.getToIndex() == 60
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -1090,7 +1706,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(TextView.class.getName())
                         && event.getText().size() > 0
@@ -1099,7 +1715,7 @@
                         && event.getFromIndex() == 25
                         && event.getToIndex() == 53
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -1125,7 +1741,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(TextView.class.getName())
                         && event.getText().size() > 0
@@ -1134,7 +1750,7 @@
                         && event.getFromIndex() == 0
                         && event.getToIndex() == 25
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -1155,6 +1771,281 @@
     }
 
     @MediumTest
+    public void testActionNextAndPreviousAtGranularityLineOverTextExtend() throws Exception {
+        final EditText editText = (EditText) getActivity().findViewById(R.id.edit);
+
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                editText.setText(getString(R.string.android_wiki_short));
+                Selection.removeSelection(editText.getText());
+            }
+        });
+
+        final AccessibilityNodeInfo text = getInteractionBridge()
+               .findAccessibilityNodeInfoByTextFromRoot(getString(R.string.android_wiki_short));
+
+        final int granularities = text.getMovementGranularities();
+        assertEquals(granularities, AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER
+                | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD
+                | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE
+                | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
+                | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+
+        final Bundle arguments = new Bundle();
+        arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
+                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
+        arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, true);
+
+        // Move to the next line and wait for an event.
+        AccessibilityEvent firstExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(EditText.class.getName())
+                        && event.getText().size() > 0
+                        && event.getText().get(0).toString().equals(getString(
+                                R.string.android_wiki_short))
+                        && event.getFromIndex() == 0
+                        && event.getToIndex() == 19
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(firstExpected);
+
+        // Verify the selection position.
+        assertEquals(0, Selection.getSelectionStart(editText.getText()));
+        assertEquals(19, Selection.getSelectionEnd(editText.getText()));
+
+        // Move to the next line and wait for an event.
+        AccessibilityEvent secondExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(EditText.class.getName())
+                        && event.getText().size() > 0
+                        && event.getText().get(0).toString().equals(getString(
+                                R.string.android_wiki_short))
+                        && event.getFromIndex() == 19
+                        && event.getToIndex() == 35
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(secondExpected);
+
+        // Verify the selection position.
+        assertEquals(0, Selection.getSelectionStart(editText.getText()));
+        assertEquals(35, Selection.getSelectionEnd(editText.getText()));
+
+        // Move to the next line and wait for an event.
+        AccessibilityEvent thirdExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(EditText.class.getName())
+                        && event.getText().size() > 0
+                        && event.getText().get(0).toString().equals(getString(
+                                R.string.android_wiki_short))
+                        && event.getFromIndex() == 35
+                        && event.getToIndex() == 53
+                        && event.getMovementGranularity() ==
+                                 AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(thirdExpected);
+
+        // Verify the selection position.
+        assertEquals(0, Selection.getSelectionStart(editText.getText()));
+        assertEquals(53, Selection.getSelectionEnd(editText.getText()));
+
+        // Focus the view so we can change selection.
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                editText.setFocusable(true);
+                editText.requestFocus();
+            }
+        });
+
+        // Put selection at the end of the text.
+        Bundle setSelectionArgs = new Bundle();
+        setSelectionArgs.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 53);
+        setSelectionArgs.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, 53);
+        assertTrue(getInteractionBridge().performAction(text,
+                AccessibilityNodeInfo.ACTION_SET_SELECTION, setSelectionArgs));
+
+        // Verify the selection position.
+        assertEquals(53, Selection.getSelectionStart(editText.getText()));
+        assertEquals(53, Selection.getSelectionEnd(editText.getText()));
+
+        // Unocus the view so we can hide the keyboard.
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                editText.clearFocus();
+                editText.setFocusable(false);
+            }
+        });
+
+        // Move to the previous line and wait for an event.
+        AccessibilityEvent fourthExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(EditText.class.getName())
+                        && event.getText().size() > 0
+                        && event.getText().get(0).toString().equals(getString(
+                                R.string.android_wiki_short))
+                        && event.getFromIndex() == 35
+                        && event.getToIndex() == 53
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(fourthExpected);
+
+        // Verify the selection position.
+        assertEquals(35, Selection.getSelectionStart(editText.getText()));
+        assertEquals(53, Selection.getSelectionEnd(editText.getText()));
+
+        // Move to the previous line and wait for an event.
+        AccessibilityEvent fifthExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(EditText.class.getName())
+                        && event.getText().size() > 0
+                        && event.getText().get(0).toString().equals(getString(
+                                R.string.android_wiki_short))
+                        && event.getFromIndex() == 19
+                        && event.getToIndex() == 35
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(fifthExpected);
+
+        // Verify the selection position.
+        assertEquals(19, Selection.getSelectionStart(editText.getText()));
+        assertEquals(53, Selection.getSelectionEnd(editText.getText()));
+
+        // Move to the previous line and wait for an event.
+        AccessibilityEvent sixthExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                               AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(EditText.class.getName())
+                        && event.getText().size() > 0
+                        && event.getText().get(0).toString().equals(getString(
+                                R.string.android_wiki_short))
+                        && event.getFromIndex() == 0
+                        && event.getToIndex() == 19
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(sixthExpected);
+
+        // Verify the selection position.
+        assertEquals(0, Selection.getSelectionStart(editText.getText()));
+        assertEquals(53, Selection.getSelectionEnd(editText.getText()));
+
+        // Make sure there is no previous.
+        assertFalse(getInteractionBridge().performAction(text,
+                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments));
+
+        // Verify the selection position.
+        assertEquals(0, Selection.getSelectionStart(editText.getText()));
+        assertEquals(53, Selection.getSelectionEnd(editText.getText()));
+    }
+
+    @MediumTest
     public void testActionNextAndPreviousAtGranularityPageOverText() throws Exception {
         final EditText editText = (EditText) getActivity().findViewById(R.id.edit);
 
@@ -1195,7 +2086,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(EditText.class.getName())
                         && event.getText().size() > 0
@@ -1204,7 +2095,7 @@
                         && event.getFromIndex() == 0
                         && event.getToIndex() == 139
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -1230,7 +2121,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(EditText.class.getName())
                         && event.getText().size() > 0
@@ -1239,7 +2130,7 @@
                         && event.getFromIndex() == 139
                         && event.getToIndex() == 285
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -1265,7 +2156,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(EditText.class.getName())
                         && event.getText().size() > 0
@@ -1274,7 +2165,7 @@
                         && event.getFromIndex() == 285
                         && event.getToIndex() == 436
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -1308,7 +2199,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(EditText.class.getName())
                         && event.getText().size() > 0
@@ -1317,7 +2208,7 @@
                         && event.getFromIndex() == 285
                         && event.getToIndex() == 436
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -1343,7 +2234,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(EditText.class.getName())
                         && event.getText().size() > 0
@@ -1352,7 +2243,7 @@
                         && event.getFromIndex() == 139
                         && event.getToIndex() == 285
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -1378,7 +2269,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(EditText.class.getName())
                         && event.getText().size() > 0
@@ -1387,7 +2278,7 @@
                         && event.getFromIndex() == 0
                         && event.getToIndex() == 139
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -1408,6 +2299,285 @@
     }
 
     @MediumTest
+    public void testActionNextAndPreviousAtGranularityPageOverTextExtend() throws Exception {
+        final EditText editText = (EditText) getActivity().findViewById(R.id.edit);
+
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                editText.setText(getString(R.string.android_wiki));
+                Selection.removeSelection(editText.getText());
+            }
+        });
+
+        final AccessibilityNodeInfo text = getInteractionBridge()
+               .findAccessibilityNodeInfoByTextFromRoot(getString(R.string.android_wiki));
+
+        final int granularities = text.getMovementGranularities();
+        assertEquals(granularities, AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER
+                | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD
+                | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE
+                | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
+                | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+
+        final Bundle arguments = new Bundle();
+        arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
+                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+        arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, true);
+
+        // Move to the next page and wait for an event.
+        AccessibilityEvent firstExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(EditText.class.getName())
+                        && event.getText().size() > 0
+                        && event.getText().get(0).toString().equals(getString(
+                                R.string.android_wiki))
+                        && event.getFromIndex() == 0
+                        && event.getToIndex() == 139
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(firstExpected);
+
+        // Verify the selection position.
+        assertEquals(0, Selection.getSelectionStart(editText.getText()));
+        assertEquals(139, Selection.getSelectionEnd(editText.getText()));
+
+        // Move to the next page and wait for an event.
+        AccessibilityEvent secondExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(EditText.class.getName())
+                        && event.getText().size() > 0
+                        && event.getText().get(0).toString().equals(getString(
+                                R.string.android_wiki))
+                        && event.getFromIndex() == 139
+                        && event.getToIndex() == 285
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(secondExpected);
+
+        // Verify the selection position.
+        assertEquals(0, Selection.getSelectionStart(editText.getText()));
+        assertEquals(285, Selection.getSelectionEnd(editText.getText()));
+
+        // Move to the next page and wait for an event.
+        AccessibilityEvent thirdExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(EditText.class.getName())
+                        && event.getText().size() > 0
+                        && event.getText().get(0).toString().equals(getString(
+                                R.string.android_wiki))
+                        && event.getFromIndex() == 285
+                        && event.getToIndex() == 436
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(thirdExpected);
+
+        // Verify the selection position.
+        assertEquals(0, Selection.getSelectionStart(editText.getText()));
+        assertEquals(436, Selection.getSelectionEnd(editText.getText()));
+
+        // Make sure there is no next.
+        assertFalse(getInteractionBridge().performAction(text,
+                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments));
+
+        // Focus the view so we can change selection.
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                editText.setFocusable(true);
+                editText.requestFocus();
+            }
+        });
+
+        // Put selection at the end of the text.
+        Bundle setSelectionArgs = new Bundle();
+        setSelectionArgs.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 436);
+        setSelectionArgs.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, 436);
+        assertTrue(getInteractionBridge().performAction(text,
+                AccessibilityNodeInfo.ACTION_SET_SELECTION, setSelectionArgs));
+
+        // Verify the selection position.
+        assertEquals(436, Selection.getSelectionStart(editText.getText()));
+        assertEquals(436, Selection.getSelectionEnd(editText.getText()));
+
+        // Unfocus the view so we can hide the soft-keyboard.
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                editText.clearFocus();
+                editText.setFocusable(false);
+            }
+        });
+
+        // Move to the previous page and wait for an event.
+        AccessibilityEvent fourthExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                                 AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(EditText.class.getName())
+                        && event.getText().size() > 0
+                        && event.getText().get(0).toString().equals(getString(
+                                R.string.android_wiki))
+                        && event.getFromIndex() == 285
+                        && event.getToIndex() == 436
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(fourthExpected);
+
+        // Verify the selection position.
+        assertEquals(285, Selection.getSelectionStart(editText.getText()));
+        assertEquals(436, Selection.getSelectionEnd(editText.getText()));
+
+        // Move to the previous page and wait for an event.
+        AccessibilityEvent fifthExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(EditText.class.getName())
+                        && event.getText().size() > 0
+                        && event.getText().get(0).toString().equals(getString(
+                                R.string.android_wiki))
+                        && event.getFromIndex() == 139
+                        && event.getToIndex() == 285
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(fifthExpected);
+
+        // Verify the selection position.
+        assertEquals(139, Selection.getSelectionStart(editText.getText()));
+        assertEquals(436, Selection.getSelectionEnd(editText.getText()));
+
+        // Move to the previous page and wait for an event.
+        AccessibilityEvent sixthExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(EditText.class.getName())
+                        && event.getText().size() > 0
+                        && event.getText().get(0).toString().equals(getString(
+                                R.string.android_wiki))
+                        && event.getFromIndex() == 0
+                        && event.getToIndex() == 139
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(sixthExpected);
+
+        // Verify the selection position.
+        assertEquals(0, Selection.getSelectionStart(editText.getText()));
+        assertEquals(436, Selection.getSelectionEnd(editText.getText()));
+
+        // Make sure there is no previous.
+        assertFalse(getInteractionBridge().performAction(text,
+                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments));
+
+        // Verify the selection position.
+        assertEquals(0, Selection.getSelectionStart(editText.getText()));
+        assertEquals(436, Selection.getSelectionEnd(editText.getText()));
+    }
+
+    @MediumTest
     public void testActionNextAndPreviousAtGranularityParagraphOverText() throws Exception {
         final TextView textView = (TextView) getActivity().findViewById(R.id.edit);
 
@@ -1447,7 +2617,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(EditText.class.getName())
                         && event.getText().size() > 0
@@ -1455,8 +2625,8 @@
                                 R.string.android_wiki_paragraphs))
                         && event.getFromIndex() == 2
                         && event.getToIndex() == 104
-                        && event.getMovementGranularity()
-                            == AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
+                        && event.getMovementGranularity() ==
+                                 AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -1482,7 +2652,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                                 AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(EditText.class.getName())
                         && event.getText().size() > 0
@@ -1491,7 +2661,7 @@
                         && event.getFromIndex() == 106
                         && event.getToIndex() == 267
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -1517,7 +2687,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(EditText.class.getName())
                         && event.getText().size() > 0
@@ -1526,7 +2696,7 @@
                         && event.getFromIndex() == 268
                         && event.getToIndex() == 582
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -1560,7 +2730,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(EditText.class.getName())
                         && event.getText().size() > 0
@@ -1569,7 +2739,7 @@
                         && event.getFromIndex() == 268
                         && event.getToIndex() == 582
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
+                                 AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -1595,7 +2765,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(EditText.class.getName())
                         && event.getText().size() > 0
@@ -1604,7 +2774,7 @@
                         && event.getFromIndex() == 106
                         && event.getToIndex() == 267
                         && event.getMovementGranularity() ==
-                            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -1630,7 +2800,7 @@
                 (event.getEventType() ==
                     AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
                         && event.getAction() ==
-                            AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
                         && event.getPackageName().equals(getActivity().getPackageName())
                         && event.getClassName().equals(EditText.class.getName())
                         && event.getText().size() > 0
@@ -1638,8 +2808,8 @@
                                 R.string.android_wiki_paragraphs))
                         && event.getFromIndex() == 2
                         && event.getToIndex() == 104
-                        && event.getMovementGranularity()
-                                == AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
             }
         }, TIMEOUT_ASYNC_PROCESSING);
 
@@ -1658,4 +2828,349 @@
         assertEquals(2, Selection.getSelectionStart(textView.getText()));
         assertEquals(2, Selection.getSelectionEnd(textView.getText()));
     }
+
+    @MediumTest
+    public void testActionNextAndPreviousAtGranularityParagraphOverTextExtend() throws Exception {
+        final EditText editText = (EditText) getActivity().findViewById(R.id.edit);
+
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                editText.setText(getString(R.string.android_wiki_paragraphs));
+                Selection.removeSelection(editText.getText());
+            }
+        });
+
+        final AccessibilityNodeInfo text = getInteractionBridge()
+               .findAccessibilityNodeInfoByTextFromRoot(getString(R.string.android_wiki_short));
+
+        final int granularities = text.getMovementGranularities();
+        assertEquals(granularities, AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER
+                | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD
+                | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE
+                | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
+                | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+
+        final Bundle arguments = new Bundle();
+        arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
+                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
+        arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, true);
+
+        // Move to the next paragraph and wait for an event.
+        AccessibilityEvent firstExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(EditText.class.getName())
+                        && event.getText().size() > 0
+                        && event.getText().get(0).toString().equals(getString(
+                                R.string.android_wiki_paragraphs))
+                        && event.getFromIndex() == 2
+                        && event.getToIndex() == 104
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(firstExpected);
+
+        // Verify the selection position.
+        assertEquals(2, Selection.getSelectionStart(editText.getText()));
+        assertEquals(104, Selection.getSelectionEnd(editText.getText()));
+
+        // Move to the next paragraph and wait for an event.
+        AccessibilityEvent secondExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(EditText.class.getName())
+                        && event.getText().size() > 0
+                        && event.getText().get(0).toString().equals(getString(
+                                R.string.android_wiki_paragraphs))
+                        && event.getFromIndex() == 106
+                        && event.getToIndex() == 267
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(secondExpected);
+
+        // Verify the selection position.
+        assertEquals(2, Selection.getSelectionStart(editText.getText()));
+        assertEquals(267, Selection.getSelectionEnd(editText.getText()));
+
+        // Move to the next paragraph and wait for an event.
+        AccessibilityEvent thirdExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(EditText.class.getName())
+                        && event.getText().size() > 0
+                        && event.getText().get(0).toString().equals(getString(
+                                R.string.android_wiki_paragraphs))
+                        && event.getFromIndex() == 268
+                        && event.getToIndex() == 582
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(thirdExpected);
+
+        // Verify the selection position.
+        assertEquals(2, Selection.getSelectionStart(editText.getText()));
+        assertEquals(582, Selection.getSelectionEnd(editText.getText()));
+
+        // Make sure there is no next.
+        assertFalse(getInteractionBridge().performAction(text,
+                AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments));
+
+        // Verify the selection position.
+        assertEquals(2, Selection.getSelectionStart(editText.getText()));
+        assertEquals(582, Selection.getSelectionEnd(editText.getText()));
+
+        // Focus the view so we can change selection.
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                editText.setFocusable(true);
+                editText.requestFocus();
+            }
+        });
+
+        // Put selection at the end of the text.
+        Bundle setSelectionArgs = new Bundle();
+        setSelectionArgs.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 582);
+        setSelectionArgs.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, 582);
+        assertTrue(getInteractionBridge().performAction(text,
+                AccessibilityNodeInfo.ACTION_SET_SELECTION, setSelectionArgs));
+
+        // Verify the selection position.
+        assertEquals(582, Selection.getSelectionStart(editText.getText()));
+        assertEquals(582, Selection.getSelectionEnd(editText.getText()));
+
+        // Unfocus the view so we can get rid of the soft-keyboard.
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                editText.clearFocus();
+                editText.setFocusable(false);
+            }
+        });
+
+        // Move to the previous paragraph and wait for an event.
+        AccessibilityEvent fourthExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(EditText.class.getName())
+                        && event.getText().size() > 0
+                        && event.getText().get(0).toString().equals(getString(
+                                R.string.android_wiki_paragraphs))
+                        && event.getFromIndex() == 268
+                        && event.getToIndex() == 582
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(fourthExpected);
+
+        // Verify the selection position.
+        assertEquals(268, Selection.getSelectionStart(editText.getText()));
+        assertEquals(582, Selection.getSelectionEnd(editText.getText()));
+
+        // Move to the previous paragraph and wait for an event.
+        AccessibilityEvent fifthExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(EditText.class.getName())
+                        && event.getText().size() > 0
+                        && event.getText().get(0).toString().equals(getString(
+                                R.string.android_wiki_paragraphs))
+                        && event.getFromIndex() == 106
+                        && event.getToIndex() == 267
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(fifthExpected);
+
+        // Verify the selection position.
+        assertEquals(106, Selection.getSelectionStart(editText.getText()));
+        assertEquals(582, Selection.getSelectionEnd(editText.getText()));
+
+        // Move to the previous paragraph and wait for an event.
+        AccessibilityEvent sixthExpected = getInteractionBridge()
+                .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+            @Override
+            public void run() {
+                getInteractionBridge().performAction(text,
+                        AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+            }
+        }, new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return
+                (event.getEventType() ==
+                    AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+                        && event.getAction() ==
+                                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+                        && event.getPackageName().equals(getActivity().getPackageName())
+                        && event.getClassName().equals(EditText.class.getName())
+                        && event.getText().size() > 0
+                        && event.getText().get(0).toString().equals(getString(
+                                R.string.android_wiki_paragraphs))
+                        && event.getFromIndex() == 2
+                        && event.getToIndex() == 104
+                        && event.getMovementGranularity() ==
+                                AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
+            }
+        }, TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure we got the expected event.
+        assertNotNull(sixthExpected);
+
+        // Verify the selection position.
+        assertEquals(2, Selection.getSelectionStart(editText.getText()));
+        assertEquals(582, Selection.getSelectionEnd(editText.getText()));
+
+        // Make sure there is no previous.
+        assertFalse(getInteractionBridge().performAction(text,
+                AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments));
+
+        // Verify the selection position.
+        assertEquals(2, Selection.getSelectionStart(editText.getText()));
+        assertEquals(582, Selection.getSelectionEnd(editText.getText()));
+    }
+
+    @MediumTest
+    public void testTextEditingActions() throws Exception {
+        final EditText editText = (EditText) getActivity().findViewById(R.id.edit);
+        final String textContent = getString(R.string.foo_bar_baz);
+
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                editText.setFocusable(true);
+                editText.requestFocus();
+                editText.setText(getString(R.string.foo_bar_baz));
+                Selection.removeSelection(editText.getText());
+            }
+        });
+
+        final AccessibilityNodeInfo text = getInteractionBridge()
+               .findAccessibilityNodeInfoByTextFromRoot(getString(R.string.foo_bar_baz));
+
+        // Select all text.
+        Bundle arguments = new Bundle();
+        arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 0);
+        arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT,
+                editText.getText().length());
+        assertTrue(getInteractionBridge().performAction(text,
+                AccessibilityNodeInfo.ACTION_SET_SELECTION, arguments));
+
+        // Copy the selected text.
+        getInteractionBridge().performAction(text, AccessibilityNodeInfo.ACTION_COPY);
+
+        // Set selection at the end.
+        final int textLength = editText.getText().length();
+        arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, textLength);
+        arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, textLength);
+        assertTrue(getInteractionBridge().performAction(text,
+                AccessibilityNodeInfo.ACTION_SET_SELECTION, arguments));
+
+        // Verify the selection position.
+        assertEquals(textLength, Selection.getSelectionStart(editText.getText()));
+        assertEquals(textLength, Selection.getSelectionEnd(editText.getText()));
+
+        // Paste the selected text.
+        assertTrue(getInteractionBridge().performAction(text,
+                AccessibilityNodeInfo.ACTION_PASTE));
+
+        // Verify the content.
+        assertEquals(editText.getText().toString(), textContent + " " + textContent);
+
+        // Select all text.
+        arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 0);
+        arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT,
+                editText.getText().length());
+        assertTrue(getInteractionBridge().performAction(text,
+                AccessibilityNodeInfo.ACTION_SET_SELECTION, arguments));
+
+        // Cut the selected text.
+        assertTrue(getInteractionBridge().performAction(text,
+                AccessibilityNodeInfo.ACTION_CUT));
+
+        // Verify the content.
+        assertTrue(TextUtils.isEmpty(editText.getText()));
+    }
 }
diff --git a/tests/tests/app/Android.mk b/tests/tests/app/Android.mk
index 8d5877d..d4387b4 100644
--- a/tests/tests/app/Android.mk
+++ b/tests/tests/app/Android.mk
@@ -21,7 +21,7 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common
+LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common voip-common
 
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
diff --git a/tests/tests/content/src/android/content/cts/ContentProviderTest.java b/tests/tests/content/src/android/content/cts/ContentProviderTest.java
index 2baa904..34b1e8c 100644
--- a/tests/tests/content/src/android/content/cts/ContentProviderTest.java
+++ b/tests/tests/content/src/android/content/cts/ContentProviderTest.java
@@ -249,12 +249,12 @@
 
         IContentProvider iContentProvider = new IContentProvider() {
             @Override
-            public int bulkInsert(Uri url, ContentValues[] initialValues) {
+            public int bulkInsert(String callingPkg, Uri url, ContentValues[] initialValues) {
                 return 0;
             }
 
             @Override
-            public int delete(Uri url, String selection, String[] selectionArgs) {
+            public int delete(String callingPkg, Uri url, String selection, String[] selectionArgs) {
                 return 0;
             }
 
@@ -264,36 +264,36 @@
             }
 
             @Override
-            public Uri insert(Uri url, ContentValues initialValues) {
+            public Uri insert(String callingPkg, Uri url, ContentValues initialValues) {
                 return null;
             }
 
             @Override
-            public ParcelFileDescriptor openFile(Uri url, String mode) {
+            public ParcelFileDescriptor openFile(String callingPkg, Uri url, String mode) {
                 return null;
             }
 
             @Override
-            public AssetFileDescriptor openAssetFile(Uri url, String mode) {
+            public AssetFileDescriptor openAssetFile(String callingPkg, Uri url, String mode) {
                 return null;
             }
 
             @Override
-            public ContentProviderResult[] applyBatch(
+            public ContentProviderResult[] applyBatch(String callingPkg,
                     ArrayList<ContentProviderOperation> operations)
                     throws RemoteException, OperationApplicationException {
                 return null;
             }
 
             @Override
-            public Cursor query(Uri url, String[] projection, String selection,
+            public Cursor query(String callingPkg, Uri url, String[] projection, String selection,
                     String[] selectionArgs, String sortOrder,
                     ICancellationSignal cancellationSignal) {
                 return null;
             }
 
             @Override
-            public int update(Uri url, ContentValues values, String selection,
+            public int update(String callingPkg, Uri url, ContentValues values, String selection,
                     String[] selectionArgs) {
                 return 0;
             }
@@ -304,7 +304,7 @@
             }
 
             @Override
-            public Bundle call(String method, String request, Bundle args) {
+            public Bundle call(String callingPkg, String method, String request, Bundle args) {
                 return null;
             }
 
@@ -314,7 +314,8 @@
             }
 
             @Override
-            public AssetFileDescriptor openTypedAssetFile(Uri url, String mimeType, Bundle opts)
+            public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri url,
+                    String mimeType, Bundle opts)
                     throws RemoteException, FileNotFoundException {
                 return null;
             }
diff --git a/suite/pts/hostTests/browser/browserlauncher/Android.mk b/tests/tests/display/Android.mk
similarity index 66%
copy from suite/pts/hostTests/browser/browserlauncher/Android.mk
copy to tests/tests/display/Android.mk
index 0592017..ec5b40d 100644
--- a/suite/pts/hostTests/browser/browserlauncher/Android.mk
+++ b/tests/tests/display/Android.mk
@@ -13,21 +13,25 @@
 # limitations under the License.
 
 LOCAL_PATH:= $(call my-dir)
+
 include $(CLEAR_VARS)
 
 # don't include this package in any target
 LOCAL_MODULE_TAGS := optional
+# and when built explicitly put it in the data partition
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
-LOCAL_STATIC_JAVA_LIBRARIES := ptsutil ctsutil ctstestrunner ctstestserver
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_PACKAGE_NAME := PtsDeviceBrowserLauncher
+LOCAL_PACKAGE_NAME := CtsDisplayTestCases
 
-LOCAL_SDK_VERSION := 16
+LOCAL_SDK_VERSION := current
 
-include $(BUILD_PTS_PACKAGE)
+# This test runner sets up/cleans up the device before/after running the tests.
+LOCAL_CTS_TEST_RUNNER := com.android.cts.tradefed.testtype.DisplayTestRunner
 
-include $(call all-makefiles-under,$(LOCAL_PATH))
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/display/AndroidManifest.xml b/tests/tests/display/AndroidManifest.xml
new file mode 100644
index 0000000..1ddce5a
--- /dev/null
+++ b/tests/tests/display/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.cts.display">
+
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+                     android:targetPackage="com.android.cts.display"
+                     android:label="CTS tests of android.view.display"/>
+
+</manifest>
+
diff --git a/tests/tests/display/src/android/display/cts/DisplayTest.java b/tests/tests/display/src/android/display/cts/DisplayTest.java
new file mode 100644
index 0000000..595f7148
--- /dev/null
+++ b/tests/tests/display/src/android/display/cts/DisplayTest.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.display.cts;
+
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.hardware.display.DisplayManager;
+import android.test.AndroidTestCase;
+import android.util.DisplayMetrics;
+import android.view.Display;
+import android.view.WindowManager;
+
+public class DisplayTest extends AndroidTestCase {
+    // This test is called from DisplayTestRunner which brings up an overlay display on the target
+    // device. The overlay display parameters must match the ones defined there which are
+    // 1281x721/214 (wxh/dpi).
+
+    private static final int SECONDARY_DISPLAY_WIDTH = 1281;
+    private static final int SECONDARY_DISPLAY_HEIGHT = 721;
+    private static final int SECONDARY_DISPLAY_DPI = 214;
+    private static final float SCALE_DENSITY_LOWER_BOUND =
+            (float)(SECONDARY_DISPLAY_DPI - 1) / DisplayMetrics.DENSITY_DEFAULT;
+    private static final float SCALE_DENSITY_UPPER_BOUND =
+            (float)(SECONDARY_DISPLAY_DPI + 1) / DisplayMetrics.DENSITY_DEFAULT;
+    // Matches com.android.internal.R.string.display_manager_overlay_display_name.
+    private static final String OVERLAY_DISPLAY_NAME_PREFIX = "Overlay #";
+
+    private DisplayManager mDisplayManager;
+    private WindowManager mWindowManager;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mDisplayManager = (DisplayManager)mContext.getSystemService(Context.DISPLAY_SERVICE);
+        mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
+    }
+
+    private boolean isSecondarySize(Display display) {
+        final Point p = new Point();
+        display.getSize(p);
+        return p.x == SECONDARY_DISPLAY_WIDTH && p.y == SECONDARY_DISPLAY_HEIGHT;
+    }
+
+    private Display getSecondaryDisplay(Display[] displays) {
+        for (Display display : displays) {
+            if (isSecondarySize(display)) {
+                return display;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Verify that the getDisplays method returns both a default and an overlay display.
+     */
+    public void testGetDisplays() {
+        Display[] displays = mDisplayManager.getDisplays();
+        assertNotNull(displays);
+        assertTrue(2 <= displays.length);
+        boolean hasDefaultDisplay = false;
+        boolean hasSecondaryDisplay = false;
+        for (Display display : displays) {
+            if (display.getDisplayId() == Display.DEFAULT_DISPLAY) {
+                hasDefaultDisplay = true;
+            }
+            if (isSecondarySize(display)) {
+                hasSecondaryDisplay = true;
+            }
+        }
+        assertTrue(hasDefaultDisplay);
+        assertTrue(hasSecondaryDisplay);
+    }
+
+    /**
+     * Verify that the WindowManager returns the default display.
+     */
+    public void testDefaultDisplay() {
+        assertEquals(Display.DEFAULT_DISPLAY, mWindowManager.getDefaultDisplay().getDisplayId());
+    }
+
+    /**
+     * Verify that there is a secondary display.
+     */
+    public void testSecondaryDisplay() {
+        Display display = getSecondaryDisplay(mDisplayManager.getDisplays());
+        assertNotNull(display);
+        assertTrue(Display.DEFAULT_DISPLAY != display.getDisplayId());
+    }
+
+    /**
+     * Test the properties of the secondary Display.
+     */
+    public void testGetDisplayAttrs() {
+        Display display = getSecondaryDisplay(mDisplayManager.getDisplays());
+
+        assertEquals(SECONDARY_DISPLAY_WIDTH, display.getWidth());
+        assertEquals(SECONDARY_DISPLAY_HEIGHT, display.getHeight());
+
+        Point outSize = new Point();
+        display.getSize(outSize);
+        assertEquals(SECONDARY_DISPLAY_WIDTH, outSize.x);
+        assertEquals(SECONDARY_DISPLAY_HEIGHT, outSize.y);
+
+        assertEquals(0, display.getOrientation());
+
+        assertEquals(PixelFormat.RGBA_8888, display.getPixelFormat());
+
+        assertTrue(0 < display.getRefreshRate());
+
+        assertTrue(display.getName().contains(OVERLAY_DISPLAY_NAME_PREFIX));
+    }
+
+    /**
+     * Test that the getMetrics method fills in correct values.
+     */
+    public void testGetMetrics() {
+        Display display = getSecondaryDisplay(mDisplayManager.getDisplays());
+
+        Point outSize = new Point();
+        display.getSize(outSize);
+
+        DisplayMetrics outMetrics = new DisplayMetrics();
+        outMetrics.setToDefaults();
+        display.getMetrics(outMetrics);
+
+        assertEquals(SECONDARY_DISPLAY_WIDTH, outMetrics.widthPixels);
+        assertEquals(SECONDARY_DISPLAY_HEIGHT, outMetrics.heightPixels);
+
+        // The scale is in [0.1, 3], and density is the scale factor.
+        assertTrue(SCALE_DENSITY_LOWER_BOUND <= outMetrics.density
+                && outMetrics.density <= SCALE_DENSITY_UPPER_BOUND);
+        assertTrue(SCALE_DENSITY_LOWER_BOUND <= outMetrics.scaledDensity
+                && outMetrics.scaledDensity <= SCALE_DENSITY_UPPER_BOUND);
+
+        assertEquals(SECONDARY_DISPLAY_DPI, outMetrics.densityDpi);
+        assertEquals((float)SECONDARY_DISPLAY_DPI, outMetrics.xdpi);
+        assertEquals((float)SECONDARY_DISPLAY_DPI, outMetrics.ydpi);
+    }
+
+    /**
+     * Test that the getCurrentSizeRange method returns correct values.
+     */
+    public void testGetCurrentSizeRange() {
+        Display display = getSecondaryDisplay(mDisplayManager.getDisplays());
+
+        Point smallest = new Point();
+        Point largest = new Point();
+        display.getCurrentSizeRange(smallest, largest);
+
+        assertEquals(SECONDARY_DISPLAY_WIDTH, smallest.x);
+        assertEquals(SECONDARY_DISPLAY_HEIGHT, smallest.y);
+        assertEquals(SECONDARY_DISPLAY_WIDTH, largest.x);
+        assertEquals(SECONDARY_DISPLAY_HEIGHT, largest.y);
+    }
+
+    /**
+     * Test that the getFlags method returns no flag bits set for the overlay display.
+     */
+    public void testFlags() {
+        Display display = getSecondaryDisplay(mDisplayManager.getDisplays());
+
+        assertEquals(0, display.getFlags());
+    }
+}
diff --git a/suite/pts/hostTests/browser/browserlauncher/Android.mk b/tests/tests/dreams/Android.mk
similarity index 67%
copy from suite/pts/hostTests/browser/browserlauncher/Android.mk
copy to tests/tests/dreams/Android.mk
index 0592017..c630d8a 100644
--- a/suite/pts/hostTests/browser/browserlauncher/Android.mk
+++ b/tests/tests/dreams/Android.mk
@@ -13,21 +13,25 @@
 # limitations under the License.
 
 LOCAL_PATH:= $(call my-dir)
+
 include $(CLEAR_VARS)
 
-# don't include this package in any target
+LOCAL_PACKAGE_NAME := CtsDreamsTestCases
+
+# Don't include this package in any target.
 LOCAL_MODULE_TAGS := optional
 
+# When built, explicitly put it in the data partition.
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+# All tests should include android.test.runner.
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
-LOCAL_STATIC_JAVA_LIBRARIES := ptsutil ctsutil ctstestrunner ctstestserver
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_PACKAGE_NAME := PtsDeviceBrowserLauncher
+# Need access to ServiceManager
+#LOCAL_SDK_VERSION := current
 
-LOCAL_SDK_VERSION := 16
-
-include $(BUILD_PTS_PACKAGE)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/dreams/AndroidManifest.xml b/tests/tests/dreams/AndroidManifest.xml
new file mode 100644
index 0000000..fb3e564
--- /dev/null
+++ b/tests/tests/dreams/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.cts.dreams">
+
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <!-- This is a self-instrumenting test package. -->
+    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+                     android:targetPackage="com.android.cts.dreams"
+                     android:label="CTS tests for the android.service.dreams package"/>
+
+</manifest>
+
diff --git a/tests/tests/dreams/src/android/service/dreams/cts/DreamsFeatureTest.java b/tests/tests/dreams/src/android/service/dreams/cts/DreamsFeatureTest.java
new file mode 100644
index 0000000..358a7bf
--- /dev/null
+++ b/tests/tests/dreams/src/android/service/dreams/cts/DreamsFeatureTest.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.dreams.cts;
+
+import android.os.ServiceManager;
+import android.service.dreams.DreamService;
+import android.service.dreams.IDreamManager;
+
+import junit.framework.TestCase;
+
+public class DreamsFeatureTest extends TestCase {
+
+    public void testDreamManagerExists() {
+        IDreamManager service = IDreamManager.Stub.asInterface(
+                            ServiceManager.getService(DreamService.DREAM_SERVICE));
+        assertTrue("Dream manager service missing", service != null);
+    }
+
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/CameraTest.java b/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
index bbb5643..31a72cf 100755
--- a/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
@@ -53,6 +53,8 @@
 import java.util.Iterator;
 import java.util.List;
 
+import junit.framework.AssertionFailedError;
+
 /**
  * This test case must run with hardware. It can't be tested in emulator
  */
@@ -1713,6 +1715,9 @@
                 // ignore
             }
             mCamera.stopPreview();
+            // See if any frame duration violations occurred during preview run
+            AssertionFailedError e = callback.getDurationException();
+            if (e != null) throw(e);
         }
 
         // Test the invalid fps cases.
@@ -1741,6 +1746,7 @@
         // An array storing the arrival time of the frames in the last second.
         private ArrayList<Long> mFrames = new ArrayList<Long>();
         private long firstFrameArrivalTime;
+        private AssertionFailedError mDurationException = null;
 
         public void reset(double minFps, double maxFps) {
             this.mMinFps = minFps;
@@ -1752,6 +1758,7 @@
                     + ". Max frame interval=" + mMaxFrameInterval);
             mFrames.clear();
             firstFrameArrivalTime = 0;
+            mDurationException = null;
         }
 
         // This method tests if the actual fps is between minimum and maximum.
@@ -1783,34 +1790,50 @@
                 // variance is averaged out.
 
                 // Check if the frame interval is too large or too small.
-                // x100 = percent, intervalMargin should be bigger than fpsMargin considering that
-                // fps will be in the order of 10.
+                // x100 = percent, intervalMargin should be bigger than
+                // fpsMargin considering that fps will be in the order of 10.
                 double intervalMargin = 0.9;
                 long lastArrivalTime = mFrames.get(mFrames.size() - 1);
                 double interval = arrivalTime - lastArrivalTime;
                 if (LOGV) Log.v(TAG, "Frame interval=" + interval);
-                assertTrue("Frame interval (" + interval + "ms) is too large." +
-                        " mMaxFrameInterval=" + mMaxFrameInterval + "ms",
-                        interval < mMaxFrameInterval * (1.0 + intervalMargin));
-                assertTrue("Frame interval (" + interval + "ms) is too small." +
-                        " mMinFrameInterval=" + mMinFrameInterval + "ms",
-                        interval > mMinFrameInterval * (1.0 - intervalMargin));
+                try {
+                    assertTrue("Frame interval (" + interval + "ms) is too " +
+                            "large. mMaxFrameInterval=" +
+                             mMaxFrameInterval + "ms",
+                            interval < mMaxFrameInterval *
+                            (1.0 + intervalMargin));
+                    assertTrue("Frame interval (" + interval + "ms) is too " +
+                            "small. mMinFrameInterval=" +
+                            mMinFrameInterval + "ms",
+                            interval > mMinFrameInterval *
+                            (1.0 - intervalMargin));
 
-                // Check if the fps is within range.
-                double fpsMargin = 0.5; // x100 = percent
-                double avgInterval = (double)(arrivalTime - mFrames.get(0))
-                        / mFrames.size();
-                double fps = 1000.0 / avgInterval;
-                assertTrue("Actual fps (" + fps + ") should be larger than " +
-                           "min fps (" + mMinFps + ")",
-                           fps >= mMinFps * (1.0 - fpsMargin));
-                assertTrue("Actual fps (" + fps + ") should be smaller than " +
-                           "max fps (" + mMaxFps + ")",
-                           fps <= mMaxFps * (1.0 + fpsMargin));
+                    // Check if the fps is within range.
+                    double fpsMargin = 0.5; // x100 = percent
+                    double avgInterval = (double)(arrivalTime - mFrames.get(0))
+                            / mFrames.size();
+                    double fps = 1000.0 / avgInterval;
+                    assertTrue("Actual fps (" + fps + ") should be larger " +
+                            "than min fps (" + mMinFps + ")",
+                            fps >= mMinFps * (1.0 - fpsMargin));
+                    assertTrue("Actual fps (" + fps + ") should be smaller" +
+                            "than max fps (" + mMaxFps + ")",
+                            fps <= mMaxFps * (1.0 + fpsMargin));
+                } catch (AssertionFailedError e) {
+                    // Need to throw this only in the test body, instead of in
+                    // the callback
+                    if (mDurationException == null) {
+                        mDurationException = e;
+                    }
+                }
             }
             // Add the arrival time of this frame to the list.
             mFrames.add(arrivalTime);
         }
+
+        public AssertionFailedError getDurationException() {
+            return mDurationException;
+        }
     }
 
     private void assertEquals(Size expected, Size actual) {
diff --git a/tests/tests/location/src/android/location/cts/LocationManagerTest.java b/tests/tests/location/src/android/location/cts/LocationManagerTest.java
index edfda5a..5e823bc 100755
--- a/tests/tests/location/src/android/location/cts/LocationManagerTest.java
+++ b/tests/tests/location/src/android/location/cts/LocationManagerTest.java
@@ -291,7 +291,7 @@
     }
 
     /**
-     * Helper method to test a location update with given provider
+     * Helper method to test a location update with given mock location provider
      *
      * @param providerName name of provider to test. Must already exist.
      * @throws InterruptedException
@@ -322,6 +322,7 @@
         assertEquals(providerName, location.getProvider());
         assertEquals(latitude1, location.getLatitude());
         assertEquals(longitude1, location.getLongitude());
+        assertEquals(true, location.isFromMockProvider());
 
         // update location without notifying listener
         listener.reset();
@@ -373,6 +374,7 @@
         assertEquals(TEST_MOCK_PROVIDER_NAME, location.getProvider());
         assertEquals(latitude1, location.getLatitude());
         assertEquals(longitude1, location.getLongitude());
+        assertEquals(true, location.isFromMockProvider());
 
         // update location without notifying listener
         mManager.removeUpdates(listener);
@@ -420,6 +422,7 @@
         assertEquals(TEST_MOCK_PROVIDER_NAME, location.getProvider());
         assertEquals(latitude1, location.getLatitude());
         assertEquals(longitude1, location.getLongitude());
+        assertEquals(true, location.isFromMockProvider());
 
         // update location without receiving broadcast.
         mManager.removeUpdates(mPendingIntent);
@@ -509,6 +512,7 @@
         assertEquals(TEST_MOCK_PROVIDER_NAME, location.getProvider());
         assertEquals(latitude1, location.getLatitude());
         assertEquals(longitude1, location.getLongitude());
+        assertEquals(true, location.isFromMockProvider());
 
         mIntentReceiver.clearReceivedIntents();
         updateLocation(latitude2, longitude2);
@@ -519,6 +523,7 @@
         assertEquals(TEST_MOCK_PROVIDER_NAME, location.getProvider());
         assertEquals(latitude2, location.getLatitude());
         assertEquals(longitude2, location.getLongitude());
+        assertEquals(true, location.isFromMockProvider());
 
         try {
             mManager.getLastKnownLocation(null);
@@ -678,6 +683,7 @@
         assertEquals(providerName, location.getProvider());
         assertEquals(latitude, location.getLatitude());
         assertEquals(longitude, location.getLongitude());
+        assertEquals(true, location.isFromMockProvider());
 
         // Remove the listener.
         mManager.removeUpdates(listener);
diff --git a/tests/tests/media/src/android/media/cts/DecoderTest.java b/tests/tests/media/src/android/media/cts/DecoderTest.java
index ac80538..54a670d 100644
--- a/tests/tests/media/src/android/media/cts/DecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/DecoderTest.java
@@ -18,29 +18,28 @@
 
 import com.android.cts.media.R;
 
-import android.content.Context;
 import android.content.res.AssetFileDescriptor;
 import android.content.res.Resources;
 import android.media.MediaCodec;
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
-import android.test.AndroidTestCase;
 import android.util.Log;
+import android.view.Surface;
 
 import java.io.BufferedInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.ByteBuffer;
-import java.util.Map;
+import java.util.zip.CRC32;
 
-public class DecoderTest extends AndroidTestCase {
+public class DecoderTest extends MediaPlayerTestBase {
     private static final String TAG = "DecoderTest";
 
     private Resources mResources;
 
     @Override
-    public void setContext(Context context) {
-        super.setContext(context);
+    protected void setUp() throws Exception {
+        super.setUp();
         mResources = mContext.getResources();
     }
 
@@ -91,6 +90,7 @@
             masterBuffer[i] = (short) sample;
         }
         bis.close();
+        masterFd.close();
 
         AssetFileDescriptor testFd = mResources.openRawResourceFd(testinput);
 
@@ -102,6 +102,7 @@
         extractor = new MediaExtractor();
         extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
                 testFd.getLength());
+        testFd.close();
 
         assertEquals("wrong number of tracks", 1, extractor.getTrackCount());
         MediaFormat format = extractor.getTrackFormat(0);
@@ -194,8 +195,6 @@
 
         codec.stop();
         codec.release();
-        testFd.close();
-        masterFd.close();
 
         assertEquals("wrong data size", masterLength, numBytesDecoded);
         long avgErrorSquared = (totalErrorSquared / (numBytesDecoded / 2));
@@ -203,5 +202,494 @@
         assertTrue("decoding error too big: " + rmse, rmse <= maxerror);
     }
 
+    public void testCodecBasicH264() throws Exception {
+        Surface s = getActivity().getSurfaceHolder().getSurface();
+        int frames1 = countFrames(
+                R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz,
+                false, s);
+        assertEquals("wrong number of frames decoded", 240, frames1);
+
+        int frames2 = countFrames(
+                R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz,
+                false, null);
+        assertEquals("different number of frames when using Surface", frames1, frames2);
+    }
+
+    public void testCodecBasicH263() throws Exception {
+        Surface s = getActivity().getSurfaceHolder().getSurface();
+        int frames1 = countFrames(
+                R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz,
+                false, s);
+        assertEquals("wrong number of frames decoded", 122, frames1);
+
+        int frames2 = countFrames(
+                R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz,
+                false, null);
+        assertEquals("different number of frames when using Surface", frames1, frames2);
+    }
+
+    public void testCodecReconfigH264WithoutSurface() throws Exception {
+        testCodecReconfig(
+                R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, null);
+    }
+
+    public void testCodecReconfigH264WithSurface() throws Exception {
+        Surface s = getActivity().getSurfaceHolder().getSurface();
+        testCodecReconfig(
+                R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, s);
+    }
+
+    public void testCodecReconfigH263WithoutSurface() throws Exception {
+        testCodecReconfig(
+                R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, null);
+    }
+
+    public void testCodecReconfigH263WithSurface() throws Exception {
+        Surface s = getActivity().getSurfaceHolder().getSurface();
+        testCodecReconfig(
+                R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, s);
+    }
+
+//    public void testCodecReconfigOgg() throws Exception {
+//        testCodecReconfig(R.raw.sinesweepogg, null);
+//    }
+//
+    public void testCodecReconfigMp3() throws Exception {
+        testCodecReconfig(R.raw.sinesweepmp3lame, null);
+    }
+
+    public void testCodecReconfigM4a() throws Exception {
+        testCodecReconfig(R.raw.sinesweepm4a, null);
+    }
+
+    private void testCodecReconfig(int video, Surface s) throws Exception {
+        int frames1 = countFrames(video, false, s);
+        int frames2 = countFrames(video, true, s);
+        assertEquals("different number of frames when reusing codec", frames1, frames2);
+    }
+
+    private int countFrames(int video, boolean reconfigure, Surface s) throws Exception {
+        int numframes = 0;
+
+        AssetFileDescriptor testFd = mResources.openRawResourceFd(video);
+
+        MediaExtractor extractor;
+        MediaCodec codec = null;
+        ByteBuffer[] codecInputBuffers;
+        ByteBuffer[] codecOutputBuffers;
+
+        extractor = new MediaExtractor();
+        extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
+                testFd.getLength());
+
+        MediaFormat format = extractor.getTrackFormat(0);
+        String mime = format.getString(MediaFormat.KEY_MIME);
+        boolean isAudio = mime.startsWith("audio/");
+
+        codec = MediaCodec.createDecoderByType(mime);
+//        if (mime.contains("avc")) {
+//            codec = MediaCodec.createByCodecName("OMX.google.h264.decoder");
+//        } else if (mime.contains("3gpp")) {
+//            codec = MediaCodec.createByCodecName("OMX.google.h263.decoder");
+//        }
+        assertNotNull("couldn't find codec", codec);
+        Log.i("@@@@", "using codec: " + codec.getName());
+        codec.configure(format, s /* surface */, null /* crypto */, 0 /* flags */);
+        codec.start();
+        codecInputBuffers = codec.getInputBuffers();
+        codecOutputBuffers = codec.getOutputBuffers();
+
+        if (reconfigure) {
+            codec.stop();
+            codec.configure(format, s /* surface */, null /* crypto */, 0 /* flags */);
+            codec.start();
+            codecInputBuffers = codec.getInputBuffers();
+            codecOutputBuffers = codec.getOutputBuffers();
+        }
+        Log.i("@@@@", "format: " + format);
+
+        extractor.selectTrack(0);
+
+        // start decoding
+        final long kTimeOutUs = 5000;
+        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+        boolean sawInputEOS = false;
+        boolean sawOutputEOS = false;
+        int deadDecoderCounter = 0;
+        while (!sawOutputEOS && deadDecoderCounter < 100) {
+            if (!sawInputEOS) {
+                int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs);
+
+                if (inputBufIndex >= 0) {
+                    ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
+
+                    int sampleSize =
+                        extractor.readSampleData(dstBuf, 0 /* offset */);
+
+                    long presentationTimeUs = 0;
+
+                    if (sampleSize < 0) {
+                        Log.d(TAG, "saw input EOS.");
+                        sawInputEOS = true;
+                        sampleSize = 0;
+                    } else {
+                        presentationTimeUs = extractor.getSampleTime();
+                    }
+
+                    int flags = extractor.getSampleFlags();
+
+                    codec.queueInputBuffer(
+                            inputBufIndex,
+                            0 /* offset */,
+                            sampleSize,
+                            presentationTimeUs,
+                            sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
+
+                    if (!sawInputEOS) {
+                        extractor.advance();
+                    }
+                }
+            }
+
+            int res = codec.dequeueOutputBuffer(info, kTimeOutUs);
+
+            deadDecoderCounter++;
+            if (res >= 0) {
+                //Log.d(TAG, "got frame, size " + info.size + "/" + info.presentationTimeUs);
+
+                // Some decoders output a 0-sized buffer at the end. Disregard those.
+                if (info.size > 0) {
+                    deadDecoderCounter = 0;
+                    if (reconfigure) {
+                        // once we've gotten some data out of the decoder, reconfigure it again
+                        reconfigure = false;
+                        numframes = 0;
+                        extractor.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
+                        sawInputEOS = false;
+                        codec.stop();
+                        codec.configure(format, s /* surface */, null /* crypto */, 0 /* flags */);
+                        codec.start();
+                        codecInputBuffers = codec.getInputBuffers();
+                        codecOutputBuffers = codec.getOutputBuffers();
+                        continue;
+                    }
+
+                    if (isAudio) {
+                        // for audio, count the number of bytes that were decoded, not the number
+                        // of access units
+                        numframes += info.size;
+                    } else {
+                        // for video, count the number of video frames
+                        numframes++;
+                    }
+                }
+                int outputBufIndex = res;
+                codec.releaseOutputBuffer(outputBufIndex, true /* render */);
+
+                if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                    Log.d(TAG, "saw output EOS.");
+                    sawOutputEOS = true;
+                }
+            } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+                codecOutputBuffers = codec.getOutputBuffers();
+
+                Log.d(TAG, "output buffers have changed.");
+            } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+                MediaFormat oformat = codec.getOutputFormat();
+
+                Log.d(TAG, "output format has changed to " + oformat);
+            } else {
+                Log.d(TAG, "no output");
+            }
+        }
+
+        codec.stop();
+        codec.release();
+        testFd.close();
+        return numframes;
+    }
+
+    public void testEOSBehaviorH264() throws Exception {
+        // this video has an I frame at 44
+        testEOSBehavior(R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, 44);
+        testEOSBehavior(R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, 45);
+        testEOSBehavior(R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, 55);
+    }
+
+    public void testEOSBehaviorH263() throws Exception {
+        // this video has an I frame every 12 frames.
+        testEOSBehavior(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 24);
+        testEOSBehavior(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 25);
+        testEOSBehavior(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 48);
+        testEOSBehavior(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 50);
+    }
+
+    private void testEOSBehavior(int movie, int stopatsample) throws Exception {
+
+        int numframes = 0;
+
+        long [] checksums = new long[stopatsample];
+
+        AssetFileDescriptor testFd = mResources.openRawResourceFd(movie);
+
+        MediaExtractor extractor;
+        MediaCodec codec = null;
+        ByteBuffer[] codecInputBuffers;
+        ByteBuffer[] codecOutputBuffers;
+
+        extractor = new MediaExtractor();
+        extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
+                testFd.getLength());
+
+        MediaFormat format = extractor.getTrackFormat(0);
+        String mime = format.getString(MediaFormat.KEY_MIME);
+        boolean isAudio = mime.startsWith("audio/");
+
+        codec = MediaCodec.createDecoderByType(mime);
+//        if (mime.contains("avc")) {
+//            codec = MediaCodec.createByCodecName("OMX.google.h264.decoder");
+//        } else if (mime.contains("3gpp")) {
+//            codec = MediaCodec.createByCodecName("OMX.google.h263.decoder");
+//        }
+        assertNotNull("couldn't find codec", codec);
+        Log.i("@@@@", "using codec: " + codec.getName());
+        codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
+        codec.start();
+        codecInputBuffers = codec.getInputBuffers();
+        codecOutputBuffers = codec.getOutputBuffers();
+
+        extractor.selectTrack(0);
+
+        // start decoding
+        final long kTimeOutUs = 5000;
+        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+        boolean sawInputEOS = false;
+        boolean sawOutputEOS = false;
+        int deadDecoderCounter = 0;
+        int samplenum = 0;
+        while (!sawOutputEOS && deadDecoderCounter < 100) {
+            if (!sawInputEOS) {
+                int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs);
+
+                if (inputBufIndex >= 0) {
+                    ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
+
+                    int sampleSize =
+                        extractor.readSampleData(dstBuf, 0 /* offset */);
+//                    Log.i("@@@@", "read sample " + samplenum + ":" + extractor.getSampleFlags()
+//                            + " @ " + extractor.getSampleTime() + " size " + sampleSize);
+                    samplenum++;
+
+                    long presentationTimeUs = 0;
+
+                    if (sampleSize < 0 || samplenum >= (stopatsample + 100)) {
+                        Log.d(TAG, "saw input EOS.");
+                        sawInputEOS = true;
+                        sampleSize = 0;
+                    } else {
+                        presentationTimeUs = extractor.getSampleTime();
+                    }
+
+                    int flags = extractor.getSampleFlags();
+
+                    codec.queueInputBuffer(
+                            inputBufIndex,
+                            0 /* offset */,
+                            sampleSize,
+                            presentationTimeUs,
+                            sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
+
+                    if (!sawInputEOS) {
+                        extractor.advance();
+                    }
+                }
+            }
+
+            int res = codec.dequeueOutputBuffer(info, kTimeOutUs);
+
+            deadDecoderCounter++;
+            if (res >= 0) {
+
+                // Some decoders output a 0-sized buffer at the end. Disregard those.
+                if (info.size > 0) {
+                    deadDecoderCounter = 0;
+
+                    if (isAudio) {
+                        // for audio, count the number of bytes that were decoded, not the number
+                        // of access units
+                        numframes += info.size;
+                    } else {
+                        // for video, count the number of video frames
+                        long sum = checksum(codecOutputBuffers[res], info.size);
+                        if (numframes < checksums.length) {
+                            checksums[numframes] = sum;
+                        }
+                        numframes++;
+                    }
+                }
+//                Log.d(TAG, "got frame, size " + info.size + "/" + info.presentationTimeUs +
+//                        "/" + numframes + "/" + info.flags);
+
+                int outputBufIndex = res;
+                codec.releaseOutputBuffer(outputBufIndex, true /* render */);
+
+                if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                    Log.d(TAG, "saw output EOS.");
+                    sawOutputEOS = true;
+                }
+            } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+                codecOutputBuffers = codec.getOutputBuffers();
+
+                Log.d(TAG, "output buffers have changed.");
+            } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+                MediaFormat oformat = codec.getOutputFormat();
+
+                Log.d(TAG, "output format has changed to " + oformat);
+            } else {
+                Log.d(TAG, "no output");
+            }
+        }
+
+        codec.stop();
+        codec.release();
+        extractor.release();
+
+
+        // We now have checksums for every frame.
+        // Now decode again, but signal EOS right before an index frame, and ensure the frames
+        // prior to that are the same.
+
+        extractor = new MediaExtractor();
+        extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
+                testFd.getLength());
+
+        codec = MediaCodec.createDecoderByType(mime);
+//        if (mime.contains("avc")) {
+//            codec = MediaCodec.createByCodecName("OMX.google.h264.decoder");
+//        } else if (mime.contains("3gpp")) {
+//            codec = MediaCodec.createByCodecName("OMX.google.h263.decoder");
+//        }
+        codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
+        codec.start();
+        codecInputBuffers = codec.getInputBuffers();
+        codecOutputBuffers = codec.getOutputBuffers();
+
+        extractor.selectTrack(0);
+
+        // start decoding
+        info = new MediaCodec.BufferInfo();
+        sawInputEOS = false;
+        sawOutputEOS = false;
+        deadDecoderCounter = 0;
+        samplenum = 0;
+        numframes = 0;
+        while (!sawOutputEOS && deadDecoderCounter < 100) {
+            if (!sawInputEOS) {
+                int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs);
+
+                if (inputBufIndex >= 0) {
+                    ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
+
+                    int sampleSize =
+                        extractor.readSampleData(dstBuf, 0 /* offset */);
+//                    Log.i("@@@@", "read sample " + samplenum + ":" + extractor.getSampleFlags()
+//                            + " @ " + extractor.getSampleTime() + " size " + sampleSize);
+                    samplenum++;
+
+                    long presentationTimeUs = extractor.getSampleTime();
+
+                    if (sampleSize < 0 || samplenum >= stopatsample) {
+                        Log.d(TAG, "saw input EOS.");
+                        sawInputEOS = true;
+                        if (sampleSize < 0) {
+                            sampleSize = 0;
+                        }
+                    }
+
+                    int flags = extractor.getSampleFlags();
+
+                    codec.queueInputBuffer(
+                            inputBufIndex,
+                            0 /* offset */,
+                            sampleSize,
+                            presentationTimeUs,
+                            sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
+
+                    if (!sawInputEOS) {
+                        extractor.advance();
+                    }
+                }
+            }
+
+            int res = codec.dequeueOutputBuffer(info, kTimeOutUs);
+
+            deadDecoderCounter++;
+            if (res >= 0) {
+
+                // Some decoders output a 0-sized buffer at the end. Disregard those.
+                if (info.size > 0) {
+                    deadDecoderCounter = 0;
+
+                    if (isAudio) {
+                        // for audio, count the number of bytes that were decoded, not the number
+                        // of access units
+                        numframes += info.size;
+                    } else {
+                        // for video, count the number of video frames
+                        long sum = checksum(codecOutputBuffers[res], info.size);
+                        if (numframes < checksums.length) {
+                            assertEquals("frame data mismatch at frame " + numframes,
+                                    checksums[numframes], sum);
+                        }
+                        numframes++;
+                    }
+                }
+//                Log.d(TAG, "got frame, size " + info.size + "/" + info.presentationTimeUs +
+//                        "/" + numframes + "/" + info.flags);
+
+                int outputBufIndex = res;
+                codec.releaseOutputBuffer(outputBufIndex, true /* render */);
+
+                if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                    Log.d(TAG, "saw output EOS.");
+                    sawOutputEOS = true;
+                }
+            } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+                codecOutputBuffers = codec.getOutputBuffers();
+
+                Log.d(TAG, "output buffers have changed.");
+            } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+                MediaFormat oformat = codec.getOutputFormat();
+
+                Log.d(TAG, "output format has changed to " + oformat);
+            } else {
+                Log.d(TAG, "no output");
+            }
+        }
+
+        codec.stop();
+        codec.release();
+        extractor.release();
+
+        assertEquals("I!=O", samplenum, numframes);
+        assertTrue("last frame didn't have EOS", sawOutputEOS);
+        assertEquals(stopatsample, numframes);
+
+        testFd.close();
+    }
+
+    private long checksum(ByteBuffer buf, int size) {
+        assertTrue(size != 0);
+        assertTrue(size <= buf.capacity());
+        CRC32 crc = new CRC32();
+        int pos = buf.position();
+        buf.rewind();
+        for (int i = 0; i < buf.capacity(); i++) {
+            crc.update(buf.get());
+        }
+        buf.position(pos);
+        return crc.getValue();
+    }
+
 }
 
diff --git a/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java b/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java
new file mode 100644
index 0000000..6a7e166
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java
@@ -0,0 +1,1113 @@
+/*
+ * Copyright (C) 2013 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.media.cts;
+
+import android.graphics.SurfaceTexture;
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecList;
+import android.media.MediaFormat;
+import android.opengl.EGL14;
+import android.opengl.GLES20;
+import android.opengl.GLES11Ext;
+import android.opengl.GLSurfaceView;
+import android.opengl.Matrix;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import android.view.Surface;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.util.Arrays;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.egl.EGLSurface;
+import javax.microedition.khronos.opengles.GL;
+import javax.microedition.khronos.opengles.GL10;
+
+
+/**
+ * Generates a series of video frames, encodes them, decodes them, and tests for significant
+ * divergence from the original.
+ * <p>
+ * There are two ways to connect an encoder to a decoder.  The first is to pass the output
+ * buffers from the encoder to the input buffers of the decoder, using ByteBuffer.put() to
+ * copy the bytes.  With this approach, we need to watch for BUFFER_FLAG_CODEC_CONFIG, and
+ * if seen we use format.setByteBuffer("csd-0") followed by decoder.configure() to pass the
+ * meta-data through.
+ * <p>
+ * The second way is to write the buffers to a file and then stream it back in.  With this
+ * approach it is necessary to use a MediaExtractor to retrieve the format info and skip past
+ * the meta-data.
+ * <p>
+ * The former can be done entirely in memory, but requires that the encoder and decoder
+ * operate simultaneously (the I/O buffers are owned by MediaCodec).  The latter requires
+ * writing to disk, because MediaExtractor can only accept a file or URL as a source.
+ * <p>
+ * The direct encoder-to-decoder approach isn't currently tested elsewhere in this CTS
+ * package, so we use that here.
+ */
+public class EncodeDecodeTest extends AndroidTestCase {
+    private static final String TAG = "EncodeDecodeTest";
+    private static final boolean VERBOSE = false;           // lots of logging
+    private static final boolean DEBUG_SAVE_FILE = false;   // save copy of encoded movie
+    private static final String DEBUG_FILE_NAME_BASE = "/sdcard/test.";
+
+    // parameters for the encoder
+    private static final String MIME_TYPE = "video/avc";    // H.264 Advanced Video Coding
+    private static final int BIT_RATE = 1000000;            // 1Mbps
+    private static final int FRAME_RATE = 15;               // 15fps
+    private static final int IFRAME_INTERVAL = 10;          // 10 seconds between I-frames
+
+    // movie length, in frames
+    private static final int NUM_FRAMES = 30;               // two seconds of video
+
+    private static final int TEST_Y = 240;                  // YUV values for colored rect
+    private static final int TEST_U = 220;
+    private static final int TEST_V = 200;
+    private static final int TEST_R0 = 0;                   // RGB eqivalent of {0,0,0}
+    private static final int TEST_G0 = 136;
+    private static final int TEST_B0 = 0;
+    private static final int TEST_R1 = 255;                 // RGB equivalent of {240,220,200}
+    private static final int TEST_G1 = 166;
+    private static final int TEST_B1 = 255;
+
+    // size of a frame, in pixels
+    private int mWidth = -1;
+    private int mHeight = -1;
+
+
+    /**
+     * Tests streaming of AVC video through the encoder and decoder.  Data is encoded from
+     * a series of byte[] buffers and decoded into ByteBuffers.  The output is checked for
+     * validity.
+     */
+    public void testEncodeDecodeVideoFromBufferToBufferQCIF() throws Exception {
+        setSize(176, 144);
+        testEncodeDecodeVideoFromBuffer(false);
+    }
+    public void testEncodeDecodeVideoFromBufferToBufferQVGA() throws Exception {
+        setSize(320, 240);
+        testEncodeDecodeVideoFromBuffer(false);
+    }
+    public void testEncodeDecodeVideoFromBufferToBuffer720p() throws Exception {
+        setSize(1280, 720);
+        testEncodeDecodeVideoFromBuffer(false);
+    }
+
+    /**
+     * Tests streaming of AVC video through the encoder and decoder.  Data is encoded from
+     * a series of byte[] buffers and decoded into Surfaces.  The output is checked for
+     * validity but some frames may be dropped.
+     * <p>
+     * Because of the way SurfaceTexture.OnFrameAvailableListener works, we need to run this
+     * test on a thread that doesn't have a Looper configured.  If we don't, the test will
+     * pass, but we won't actually test the output because we'll never receive the "frame
+     * available" notifications".  The CTS test framework seems to be configuring a Looper on
+     * the test thread, so we have to hand control off to a new thread for the duration of
+     * the test.
+     */
+    public void testEncodeDecodeVideoFromBufferToSurfaceQCIF() throws Throwable {
+        setSize(176, 144);
+        BufferToSurfaceWrapper.runTest(this);
+    }
+    public void testEncodeDecodeVideoFromBufferToSurfaceQVGA() throws Throwable {
+        setSize(320, 240);
+        BufferToSurfaceWrapper.runTest(this);
+    }
+    public void testEncodeDecodeVideoFromBufferToSurface720p() throws Throwable {
+        setSize(1280, 720);
+        BufferToSurfaceWrapper.runTest(this);
+
+    }
+
+    /** Wraps testEncodeDecodeVideoFromBuffer(true) */
+    private static class BufferToSurfaceWrapper implements Runnable {
+        private Throwable mThrowable;
+        private EncodeDecodeTest mTest;
+
+        private BufferToSurfaceWrapper(EncodeDecodeTest test) {
+            mTest = test;
+        }
+
+        public void run() {
+            try {
+                mTest.testEncodeDecodeVideoFromBuffer(true);
+            } catch (Throwable th) {
+                mThrowable = th;
+            }
+        }
+
+        /**
+         * Entry point.
+         */
+        public static void runTest(EncodeDecodeTest obj) throws Throwable {
+            BufferToSurfaceWrapper wrapper = new BufferToSurfaceWrapper(obj);
+            Thread th = new Thread(wrapper, "codec test");
+            th.start();
+            th.join();
+            if (wrapper.mThrowable != null) {
+                throw wrapper.mThrowable;
+            }
+        }
+    }
+
+    /**
+     * Sets the desired frame size.
+     */
+    private void setSize(int width, int height) {
+        if ((width % 16) != 0 || (height % 16) != 0) {
+            Log.w(TAG, "WARNING: width or height not multiple of 16");
+        }
+        mWidth = width;
+        mHeight = height;
+    }
+
+    /**
+     * Tests encoding and subsequently decoding video from frames generated into a buffer.
+     * <p>
+     * We encode several frames of a video test pattern using MediaCodec, then decode the
+     * output with MediaCodec and do some simple checks.
+     * <p>
+     * See http://b.android.com/37769 for a discussion of input format pitfalls.
+     */
+    private void testEncodeDecodeVideoFromBuffer(boolean toSurface) throws Exception {
+        MediaCodecInfo codecInfo = selectCodec(MIME_TYPE);
+        if (codecInfo == null) {
+            // Don't fail CTS if they don't have an AVC codec (not here, anyway).
+            Log.e(TAG, "Unable to find an appropriate codec for " + MIME_TYPE);
+            return;
+        }
+        if (VERBOSE) Log.d(TAG, "found codec: " + codecInfo.getName());
+
+        int colorFormat = selectColorFormat(codecInfo, MIME_TYPE);
+        if (VERBOSE) Log.d(TAG, "found colorFormat: " + colorFormat);
+
+        // We avoid the device-specific limitations on width and height by using values that
+        // are multiples of 16, which all tested devices seem to be able to handle.
+        MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight);
+
+        // Set some properties.  Failing to specify some of these can cause the MediaCodec
+        // configure() call to throw an unhelpful exception.
+        format.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);
+        format.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
+        format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
+        format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
+        if (VERBOSE) Log.d(TAG, "format: " + format);
+
+        // Create a MediaCodec for the desired codec, then configure it as an encoder with
+        // our desired properties.
+        MediaCodec encoder = MediaCodec.createByCodecName(codecInfo.getName());
+        encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
+        encoder.start();
+
+        // Create a MediaCodec for the decoder, just based on the MIME type.  The various
+        // format details will be passed through the csd-0 meta-data later on.
+        MediaCodec decoder = MediaCodec.createDecoderByType(MIME_TYPE);
+
+        try {
+            encodeDecodeVideoFromBuffer(encoder, colorFormat, decoder, toSurface);
+        } finally {
+            if (VERBOSE) Log.d(TAG, "releasing codecs");
+            encoder.stop();
+            decoder.stop();
+            encoder.release();
+            decoder.release();
+        }
+    }
+
+    /**
+     * Returns the first codec capable of encoding the specified MIME type, or null if no
+     * match was found.
+     */
+    private static MediaCodecInfo selectCodec(String mimeType) {
+        int numCodecs = MediaCodecList.getCodecCount();
+        for (int i = 0; i < numCodecs; i++) {
+            MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
+
+            if (!codecInfo.isEncoder()) {
+                continue;
+            }
+
+            String[] types = codecInfo.getSupportedTypes();
+            for (int j = 0; j < types.length; j++) {
+                if (types[j].equalsIgnoreCase(mimeType)) {
+                    return codecInfo;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns a color format that is supported by the codec and by this test code.  If no
+     * match is found, this throws a test failure -- the set of formats known to the test
+     * should be expanded for new platforms.
+     */
+    private static int selectColorFormat(MediaCodecInfo codecInfo, String mimeType) {
+        MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(mimeType);
+        for (int i = 0; i < capabilities.colorFormats.length; i++) {
+            int colorFormat = capabilities.colorFormats[i];
+            switch (colorFormat) {
+                // these are the formats we know how to handle for this test
+                case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
+                case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
+                case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
+                case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:
+                case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:
+                    return colorFormat;
+                default:
+                    break;
+            }
+        }
+        fail("couldn't find a good color format for " + codecInfo.getName() + " / " + mimeType);
+        return 0;   // not reached
+    }
+
+    /**
+     * Does the actual work for encoding frames from buffers of byte[].
+     */
+    private void encodeDecodeVideoFromBuffer(MediaCodec encoder, int encoderColorFormat,
+            MediaCodec decoder, boolean toSurface) {
+        final int TIMEOUT_USEC = 10000;
+        ByteBuffer[] encoderInputBuffers = encoder.getInputBuffers();
+        ByteBuffer[] encoderOutputBuffers = encoder.getOutputBuffers();
+        ByteBuffer[] decoderInputBuffers = null;
+        ByteBuffer[] decoderOutputBuffers = null;
+        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+        int decoderColorFormat = -12345;    // init to invalid value
+        int generateIndex = 0;
+        int checkIndex = 0;
+        boolean decoderConfigured = false;
+        SurfaceStuff surfaceStuff = null;
+
+        // The size of a frame of video data, in the formats we handle, is stride*sliceHeight
+        // for Y, and (stride/2)*(sliceHeight/2) for each of the Cb and Cr channels.  Application
+        // of algebra and assuming that stride==width and sliceHeight==height yields:
+        byte[] frameData = new byte[mWidth * mHeight * 3 / 2];
+
+        // Just out of curiosity.
+        long rawSize = 0;
+        long encodedSize = 0;
+
+        // Save a copy to disk.  Useful for debugging the test.
+        FileOutputStream outputStream = null;
+        if (DEBUG_SAVE_FILE) {
+            String fileName = DEBUG_FILE_NAME_BASE + mWidth + "x" + mHeight + ".mp4";
+            try {
+                outputStream = new FileOutputStream(fileName);
+                Log.d(TAG, "encoded output will be saved as " + fileName);
+            } catch (IOException ioe) {
+                Log.w(TAG, "Unable to create debug output file " + fileName);
+                throw new RuntimeException(ioe);
+            }
+        }
+
+        if (toSurface) {
+            surfaceStuff = new SurfaceStuff(mWidth, mHeight);
+        }
+
+        // Loop until the output side is done.
+        boolean inputDone = false;
+        boolean encoderDone = false;
+        boolean outputDone = false;
+        while (!outputDone) {
+            if (VERBOSE) Log.d(TAG, "loop");
+
+            // If we're not done submitting frames, generate a new one and submit it.  By
+            // doing this on every loop we're working to ensure that the encoder always has
+            // work to do.
+            //
+            // We don't really want a timeout here, but sometimes there's a delay opening
+            // the encoder device, so a short timeout can keep us from spinning hard.
+            if (!inputDone) {
+                int inputBufIndex = encoder.dequeueInputBuffer(TIMEOUT_USEC);
+                if (VERBOSE) Log.d(TAG, "inputBufIndex=" + inputBufIndex);
+                if (inputBufIndex >= 0) {
+                    long ptsUsec = generateIndex * 1000000 / FRAME_RATE;
+                    if (generateIndex == NUM_FRAMES) {
+                        // Send an empty frame with the end-of-stream flag set.  If we set EOS
+                        // on a frame with data, that frame data will be ignored, and the
+                        // output will be short one frame.
+                        encoder.queueInputBuffer(inputBufIndex, 0, 0, ptsUsec,
+                                MediaCodec.BUFFER_FLAG_END_OF_STREAM);
+                        inputDone = true;
+                        if (VERBOSE) Log.d(TAG, "sent input EOS (with zero-length frame)");
+                    } else {
+                        generateFrame(generateIndex, encoderColorFormat, frameData);
+
+                        ByteBuffer inputBuf = encoderInputBuffers[inputBufIndex];
+                        // the buffer should be sized to hold one full frame
+                        assertTrue(inputBuf.capacity() >= frameData.length);
+                        inputBuf.clear();
+                        inputBuf.put(frameData);
+
+                        encoder.queueInputBuffer(inputBufIndex, 0, frameData.length, ptsUsec, 0);
+                        if (VERBOSE) Log.d(TAG, "submitted frame " + generateIndex + " to enc");
+                    }
+                    generateIndex++;
+                } else {
+                    // either all in use, or we timed out during initial setup
+                    if (VERBOSE) Log.d(TAG, "input buffer not available");
+                }
+            }
+
+            // Check for output from the encoder.  If there's no output yet, we either need to
+            // provide more input, or we need to wait for the encoder to work its magic.  We
+            // can't actually tell which is the case, so if we can't get an output buffer right
+            // away we loop around and see if it wants more input.
+            //
+            // Once we get EOS from the encoder, we don't need to do this anymore.
+            if (!encoderDone) {
+                int encoderStatus = encoder.dequeueOutputBuffer(info, TIMEOUT_USEC);
+                if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
+                    // no output available yet
+                    if (VERBOSE) Log.d(TAG, "no output from encoder available");
+                } else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+                    // not expected for an encoder
+                    encoderOutputBuffers = encoder.getOutputBuffers();
+                    if (VERBOSE) Log.d(TAG, "encoder output buffers changed");
+                } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+                    // not expected for an encoder
+                    MediaFormat newFormat = encoder.getOutputFormat();
+                    if (VERBOSE) Log.d(TAG, "encoder output format changed: " + newFormat);
+                } else if (encoderStatus < 0) {
+                    fail("unexpected result from encoder.dequeueOutputBuffer: " + encoderStatus);
+                } else { // encoderStatus >= 0
+                    ByteBuffer encodedData = encoderOutputBuffers[encoderStatus];
+                    if (encodedData == null) {
+                        fail("encoderOutputBuffer " + encoderStatus + " was null");
+                    }
+
+                    // It's usually necessary to adjust the ByteBuffer values to match BufferInfo.
+                    encodedData.position(info.offset);
+                    encodedData.limit(info.offset + info.size);
+
+                    encodedSize += info.size;
+                    if (outputStream != null) {
+                        byte[] data = new byte[info.size];
+                        encodedData.get(data);
+                        encodedData.position(info.offset);
+                        try {
+                            outputStream.write(data);
+                        } catch (IOException ioe) {
+                            Log.w(TAG, "failed writing debug data to file");
+                            throw new RuntimeException(ioe);
+                        }
+                    }
+                    if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
+                        // Codec config info.  Only expected on first packet.
+                        assertFalse(decoderConfigured);
+                        MediaFormat format =
+                                MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight);
+                        format.setByteBuffer("csd-0", encodedData);
+                        decoder.configure(format, toSurface ? surfaceStuff.getSurface() : null,
+                                null, 0);
+                        decoder.start();
+                        decoderInputBuffers = decoder.getInputBuffers();
+                        decoderOutputBuffers = decoder.getOutputBuffers();
+                        decoderConfigured = true;
+                        if (VERBOSE) Log.d(TAG, "decoder configured (" + info.size + " bytes)");
+                    } else {
+                        // Get a decoder input buffer, blocking until it's available.
+                        assertTrue(decoderConfigured);
+                        int inputBufIndex = decoder.dequeueInputBuffer(-1);
+                        ByteBuffer inputBuf = decoderInputBuffers[inputBufIndex];
+                        inputBuf.clear();
+                        inputBuf.put(encodedData);
+                        decoder.queueInputBuffer(inputBufIndex, 0, info.size, info.presentationTimeUs,
+                                info.flags);
+
+                        encoderDone = (info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
+                        if (VERBOSE) Log.d(TAG, "passed " + info.size + " bytes to decoder"
+                                + (encoderDone ? " (EOS)" : ""));
+                    }
+
+                    encoder.releaseOutputBuffer(encoderStatus, false);
+                }
+            }
+
+            // Check for output from the decoder.  We want to do this on every loop to avoid
+            // the possibility of stalling the pipeline.  We use a short timeout to avoid
+            // burning CPU if the decoder is hard at work but the next frame isn't quite ready.
+            //
+            // If we're decoding to a Surface, we'll get notified here as usual but the
+            // ByteBuffer references will be null.  The data is sent to Surface instead.
+            if (decoderConfigured) {
+                int decoderStatus = decoder.dequeueOutputBuffer(info, TIMEOUT_USEC);
+                if (decoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
+                    // no output available yet
+                    if (VERBOSE) Log.d(TAG, "no output from decoder available");
+                } else if (decoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+                    if (VERBOSE) Log.d(TAG, "decoder output buffers changed");
+                    decoderOutputBuffers = decoder.getOutputBuffers();
+                } else if (decoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+                    // this happens before the first frame is returned
+                    MediaFormat decoderOutputFormat = decoder.getOutputFormat();
+                    decoderColorFormat =
+                            decoderOutputFormat.getInteger(MediaFormat.KEY_COLOR_FORMAT);
+                    if (VERBOSE) Log.d(TAG, "decoder output format changed: " +
+                            decoderOutputFormat);
+                } else if (decoderStatus < 0) {
+                    fail("unexpected result from deocder.dequeueOutputBuffer: " + decoderStatus);
+                } else {  // decoderStatus >= 0
+                    if (!toSurface) {
+                        ByteBuffer outputFrame = decoderOutputBuffers[decoderStatus];
+
+                        outputFrame.position(info.offset);
+                        outputFrame.limit(info.offset + info.size);
+
+                        rawSize += info.size;
+                        if (info.size == 0) {
+                            if (VERBOSE) Log.d(TAG, "got empty frame");
+                        } else {
+                            if (VERBOSE) Log.d(TAG, "decoded, checking frame " + checkIndex);
+                            checkFrame(checkIndex++, decoderColorFormat, outputFrame);
+                        }
+
+                        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                            if (VERBOSE) Log.d(TAG, "output EOS");
+                            outputDone = true;
+                        }
+                    } else {
+                        // Before we release+render this buffer, check to see if data from a
+                        // previous go-round has latched.
+                        surfaceStuff.checkNewImageIfAvailable();
+
+                        if (VERBOSE) Log.d(TAG, "surface decoder given buffer " + decoderStatus +
+                                " (size=" + info.size + ")");
+                        rawSize += info.size;
+                        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                            if (VERBOSE) Log.d(TAG, "output EOS");
+                            outputDone = true;
+                        }
+                    }
+
+                    // If output is going to a Surface, the second argument should be true.
+                    // If not, the value doesn't matter.
+                    //
+                    // If we are sending to a Surface, then some time after we call this the
+                    // data will be made available to SurfaceTexture, and the onFrameAvailable()
+                    // callback will fire.
+                    decoder.releaseOutputBuffer(decoderStatus, true /*render*/);
+                }
+            }
+        }
+
+        if (VERBOSE) Log.d(TAG, "encoded " + NUM_FRAMES + " frames at "
+                + mWidth + "x" + mHeight + ": raw=" + rawSize + ", enc=" + encodedSize);
+        if (outputStream != null) {
+            try {
+                outputStream.close();
+            } catch (IOException ioe) {
+                Log.w(TAG, "failed closing debug file");
+                throw new RuntimeException(ioe);
+            }
+        }
+    }
+
+    /**
+     * Generates data for frame N into the supplied buffer.  We have an 8-frame animation
+     * sequence that wraps around.  It looks like this:
+     * <pre>
+     *   0 1 2 3
+     *   7 6 5 4
+     * </pre>
+     * We draw one of the eight rectangles and leave the rest set to the zero-fill color.
+     */
+    private void generateFrame(int frameIndex, int colorFormat, byte[] frameData) {
+        final int HALF_WIDTH = mWidth / 2;
+        boolean semiPlanar = isSemiPlanarYUV(colorFormat);
+
+        // Set to zero.  In YUV this is a dull green.
+        Arrays.fill(frameData, (byte) 0);
+
+        int startX, startY, countX, countY;
+
+        frameIndex %= 8;
+        //frameIndex = (frameIndex / 8) % 8;    // use this instead for debug -- easier to see
+        if (frameIndex < 4) {
+            startX = frameIndex * (mWidth / 4);
+            startY = 0;
+        } else {
+            startX = (7 - frameIndex) * (mWidth / 4);
+            startY = mHeight / 2;
+        }
+
+        for (int y = startY + (mHeight/2) - 1; y >= startY; --y) {
+            for (int x = startX + (mWidth/4) - 1; x >= startX; --x) {
+                if (semiPlanar) {
+                    // full-size Y, followed by CbCr pairs at half resolution
+                    // e.g. Nexus 4 OMX.qcom.video.encoder.avc COLOR_FormatYUV420SemiPlanar
+                    // e.g. Galaxy Nexus OMX.TI.DUCATI1.VIDEO.H264E
+                    //        OMX_TI_COLOR_FormatYUV420PackedSemiPlanar
+                    frameData[y * mWidth + x] = (byte) TEST_Y;
+                    if ((x & 0x01) == 0 && (y & 0x01) == 0) {
+                        frameData[mWidth*mHeight + y * HALF_WIDTH + x] = (byte) TEST_U;
+                        frameData[mWidth*mHeight + y * HALF_WIDTH + x + 1] = (byte) TEST_V;
+                    }
+                } else {
+                    // full-size Y, followed by quarter-size Cb and quarter-size Cr
+                    // e.g. Nexus 10 OMX.Exynos.AVC.Encoder COLOR_FormatYUV420Planar
+                    // e.g. Nexus 7 OMX.Nvidia.h264.encoder COLOR_FormatYUV420Planar
+                    frameData[y * mWidth + x] = (byte) TEST_Y;
+                    if ((x & 0x01) == 0 && (y & 0x01) == 0) {
+                        frameData[mWidth*mHeight + (y/2) * HALF_WIDTH + (x/2)] = (byte) TEST_U;
+                        frameData[mWidth*mHeight + HALF_WIDTH * (mHeight / 2) +
+                                  (y/2) * HALF_WIDTH + (x/2)] = (byte) TEST_V;
+                    }
+                }
+            }
+        }
+
+        if (false) {
+            // make sure that generate and check agree
+            Log.d(TAG, "SPOT CHECK");
+            checkFrame(frameIndex, colorFormat, ByteBuffer.wrap(frameData));
+            Log.d(TAG, "SPOT CHECK DONE");
+        }
+    }
+
+    /**
+     * Performs a simple check to see if the frame is more or less right.
+     * <p>
+     * See {@link generateFrame} for a description of the layout.  The idea is to sample
+     * one pixel from the middle of the 8 regions, and verify that the correct one has
+     * the non-background color.  We can't know exactly what the video encoder has done
+     * with our frames, so we just check to see if it looks like more or less the right thing.
+     * <p>
+     * Throws a failure if the frame looks wrong.
+     */
+    private void checkFrame(int frameIndex, int colorFormat, ByteBuffer frameData) {
+        final int HALF_WIDTH = mWidth / 2;
+        boolean frameFailed = false;
+
+        if (colorFormat == 0x7FA30C03) {
+            // Nexus 4 decoder output OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka
+            Log.d(TAG, "unable to check frame contents for colorFormat=" +
+                    Integer.toHexString(colorFormat));
+            return;
+        }
+        boolean semiPlanar = isSemiPlanarYUV(colorFormat);
+
+        frameIndex %= 8;
+
+        for (int i = 0; i < 8; i++) {
+            int x, y;
+            if (i < 4) {
+                x = i * (mWidth / 4) + (mWidth / 8);
+                y = mHeight / 4;
+            } else {
+                x = (7 - i) * (mWidth / 4) + (mWidth / 8);
+                y = (mHeight * 3) / 4;
+            }
+
+            int testY, testU, testV;
+            if (semiPlanar) {
+                // Galaxy Nexus uses OMX_TI_COLOR_FormatYUV420PackedSemiPlanar
+                testY = frameData.get(y * mWidth + x) & 0xff;
+                testU = frameData.get(mWidth*mHeight + 2*(y/2) * HALF_WIDTH + 2*(x/2)) & 0xff;
+                testV = frameData.get(mWidth*mHeight + 2*(y/2) * HALF_WIDTH + 2*(x/2) + 1) & 0xff;
+            } else {
+                // Nexus 10, Nexus 7 use COLOR_FormatYUV420Planar
+                testY = frameData.get(y * mWidth + x) & 0xff;
+                testU = frameData.get(mWidth*mHeight + (y/2) * HALF_WIDTH + (x/2)) & 0xff;
+                testV = frameData.get(mWidth*mHeight + HALF_WIDTH * (mHeight / 2) +
+                        (y/2) * HALF_WIDTH + (x/2)) & 0xff;
+            }
+
+            boolean failed = false;
+            if (i == frameIndex) {
+                failed = !isColorClose(testY, TEST_Y) ||
+                         !isColorClose(testU, TEST_U) ||
+                         !isColorClose(testV, TEST_V);
+            } else {
+                // should be our zeroed-out buffer
+                failed = !isColorClose(testY, 0) ||
+                         !isColorClose(testU, 0) ||
+                         !isColorClose(testV, 0);
+            }
+            if (failed) {
+                Log.w(TAG, "Bad frame " + frameIndex + " (r=" + i + ": Y=" + testY +
+                        " U=" + testU + " V=" + testV + ")");
+                frameFailed = true;
+            }
+        }
+
+        if (frameFailed) {
+            fail("bad frame (" + frameIndex + ")");
+        }
+    }
+
+    /**
+     * Returns true if the actual color value is close to the expected color value.
+     */
+    static boolean isColorClose(int actual, int expected) {
+        if (expected < 5) {
+            return actual < (expected + 5);
+        } else if (expected > 250) {
+            return actual > (expected - 5);
+        } else {
+            return actual > (expected - 5) && actual < (expected + 5);
+        }
+    }
+
+    /**
+     * Returns true if the specified color format is semi-planar YUV.  Throws an exception
+     * if the color format is not recognized (e.g. not YUV).
+     */
+    private static boolean isSemiPlanarYUV(int colorFormat) {
+        switch (colorFormat) {
+            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
+            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
+                return false;
+            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
+            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:
+            case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:
+                return true;
+            default:
+                throw new RuntimeException("unknown format " + colorFormat);
+        }
+    }
+
+    /**
+     * Holds state associated with a Surface used for output.
+     * <p>
+     * By default, the Surface will be using a BufferQueue in asynchronous mode, so we
+     * will likely miss a number of frames.
+     */
+    private static class SurfaceStuff implements SurfaceTexture.OnFrameAvailableListener {
+        private static final int EGL_OPENGL_ES2_BIT = 4;
+
+        private EGL10 mEGL;
+        private EGLDisplay mEGLDisplay;
+        private EGLContext mEGLContext;
+        private EGLSurface mEGLSurface;
+
+        private SurfaceTexture mSurfaceTexture;
+        private Surface mSurface;
+        private boolean mFrameAvailable = false;    // guarded by "this"
+
+        private int mWidth;
+        private int mHeight;
+
+        private VideoRender mVideoRender;
+
+        public SurfaceStuff(int width, int height) {
+            mWidth = width;
+            mHeight = height;
+
+            eglSetup();
+
+            mVideoRender = new VideoRender();
+            mVideoRender.onSurfaceCreated();
+
+            // Even if we don't access the SurfaceTexture after the constructor returns, we
+            // still need to keep a reference to it.  The Surface doesn't retain a reference
+            // at the Java level, so if we don't either then the object can get GCed, which
+            // causes the native finalizer to run.
+            if (VERBOSE) Log.d(TAG, "textureID=" + mVideoRender.getTextureId());
+            mSurfaceTexture = new SurfaceTexture(mVideoRender.getTextureId());
+
+            // This doesn't work if SurfaceStuff is created on the thread that CTS started for
+            // these test cases.
+            //
+            // The CTS-created thread has a Looper, and the SurfaceTexture constructor will
+            // create a Handler that uses it.  The "frame available" message is delivered
+            // there, but since we're not a Looper-based thread we'll never see it.  For
+            // this to do anything useful, SurfaceStuff must be created on a thread without
+            // a Looper, so that SurfaceTexture uses the main application Looper instead.
+            //
+            // Java language note: passing "this" out of a constructor is generally unwise,
+            // but we should be able to get away with it here.
+            mSurfaceTexture.setOnFrameAvailableListener(this);
+
+            mSurface = new Surface(mSurfaceTexture);
+        }
+
+        /**
+         * Prepares EGL.  We want a GLES 2.0 context and a surface that supports pbuffer.
+         */
+        private void eglSetup() {
+            mEGL = (EGL10)EGLContext.getEGL();
+            mEGLDisplay = mEGL.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+            if (!mEGL.eglInitialize(mEGLDisplay, null)) {
+                fail("unable to initialize EGL10");
+            }
+
+            // Configure surface for pbuffer and OpenGL ES 2.0.  We want enough RGB bits
+            // to be able to tell if the frame is reasonable.
+            int[] attribList = {
+                    EGL10.EGL_RED_SIZE, 8,
+                    EGL10.EGL_GREEN_SIZE, 8,
+                    EGL10.EGL_BLUE_SIZE, 8,
+                    EGL10.EGL_SURFACE_TYPE, EGL10.EGL_PBUFFER_BIT,
+                    EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+                    EGL10.EGL_NONE
+            };
+            EGLConfig[] configs = new EGLConfig[1];
+            int[] numConfigs = new int[1];
+            if (!mEGL.eglChooseConfig(mEGLDisplay, attribList, configs, 1, numConfigs)) {
+                fail("unable to find RGB888+pbuffer EGL config");
+            }
+
+            // Configure context for OpenGL ES 2.0.
+            int[] attrib_list = {
+                    EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
+                    EGL10.EGL_NONE
+            };
+            mEGLContext = mEGL.eglCreateContext(mEGLDisplay, configs[0], EGL10.EGL_NO_CONTEXT,
+                    attrib_list);
+            checkEglError("eglCreateContext");
+            assertNotNull(mEGLContext);
+
+            // Create a pbuffer surface.  By using this for output, we can use glReadPixels
+            // to test values in the output.
+            int[] surfaceAttribs = {
+                    EGL10.EGL_WIDTH, mWidth,
+                    EGL10.EGL_HEIGHT, mHeight,
+                    EGL10.EGL_NONE
+            };
+            mEGLSurface = mEGL.eglCreatePbufferSurface(mEGLDisplay, configs[0], surfaceAttribs);
+            checkEglError("eglCreatePbufferSurface");
+            assertNotNull(mEGLSurface);
+
+            if (!mEGL.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) {
+                fail("eglMakeCurrent failed");
+            }
+        }
+
+        /**
+         * Checks for EGL errors.
+         */
+        private void checkEglError(String msg) {
+            boolean failed = false;
+            int error;
+            while ((error = mEGL.eglGetError()) != EGL10.EGL_SUCCESS) {
+                Log.e(TAG, msg + ": EGL error: 0x" + Integer.toHexString(error));
+                failed = true;
+            }
+            if (failed) {
+                fail("EGL error encountered (see log)");
+            }
+        }
+
+
+        /**
+         * Returns the Surface that the MediaCodec will draw onto.
+         */
+        public Surface getSurface() {
+            return mSurface;
+        }
+
+        /**
+         * Latches the next buffer into the texture if one is available, and checks it for
+         * validity.  Must be called from the thread that created the SurfaceStuff object.
+         */
+        public void checkNewImageIfAvailable() {
+            boolean newStuff = false;
+
+            synchronized (this) {
+                if (mSurfaceTexture != null && mFrameAvailable) {
+                    mFrameAvailable = false;
+                    newStuff = true;
+                }
+            }
+
+            if (newStuff) {
+                mVideoRender.checkGlError("before updateTexImage");
+                mSurfaceTexture.updateTexImage();
+                mVideoRender.onDrawFrame(mSurfaceTexture);
+                checkSurfaceFrame();
+            }
+        }
+
+        @Override
+        public void onFrameAvailable(SurfaceTexture st) {
+            if (VERBOSE) Log.d(TAG, "new frame available");
+            synchronized (this) {
+                mFrameAvailable = true;
+            }
+        }
+
+
+        /**
+         * Attempts to check the frame for correctness.
+         * <p>
+         * Our definition of "correct" is based on knowing what the frame sequence number is,
+         * which we can't reliably get by counting frames since the underlying mechanism can
+         * drop frames.  The alternative would be to use the presentation time stamp that
+         * we passed to the video encoder, but there's no way to get that from the texture.
+         * <p>
+         * All we can do is verify that it looks something like a frame we'd expect, i.e.
+         * green with exactly one pink rectangle.
+         */
+        private void checkSurfaceFrame() {
+            ByteBuffer pixelBuf = ByteBuffer.allocateDirect(4); // TODO - reuse this
+
+            int numColoredRects = 0;
+            int rectPosn = -1;
+            for (int i = 0; i < 8; i++) {
+                // Note the coordinates are inverted on the Y-axis in GL.
+                int x, y;
+                if (i < 4) {
+                    x = i * (mWidth / 4) + (mWidth / 8);
+                    y = (mHeight * 3) / 4;
+                } else {
+                    x = (7 - i) * (mWidth / 4) + (mWidth / 8);
+                    y = mHeight / 4;
+                }
+
+                GLES20.glReadPixels(x, y, 1, 1, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, pixelBuf);
+                int r = pixelBuf.get(0) & 0xff;
+                int g = pixelBuf.get(1) & 0xff;
+                int b = pixelBuf.get(2) & 0xff;
+
+                if (isColorClose(r, TEST_R0) &&
+                        isColorClose(g, TEST_G0) &&
+                        isColorClose(b, TEST_B0)) {
+                    // empty space
+                } else if (isColorClose(r, TEST_R1) &&
+                        isColorClose(g, TEST_G1) &&
+                        isColorClose(b, TEST_B1)) {
+                    // colored rect
+                    numColoredRects++;
+                    rectPosn = i;
+                } else {
+                    // wtf
+                    Log.w(TAG, "found unexpected color r=" + r + " g=" + g + " b=" + b);
+                }
+            }
+
+            if (numColoredRects != 1) {
+                fail("Found surface with colored rects != 1 (" + numColoredRects + ")");
+            } else {
+                if (VERBOSE) Log.d(TAG, "good surface, looks like index " + rectPosn);
+            }
+        }
+    }
+
+    /**
+     * GL code to fill a surface with a texture.  This class was largely copied from
+     * VideoSurfaceView.VideoRender.
+     * <p>
+     * TODO: merge implementations
+     */
+    private static class VideoRender {
+        private static final int FLOAT_SIZE_BYTES = 4;
+        private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
+        private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
+        private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
+        private final float[] mTriangleVerticesData = {
+            // X, Y, Z, U, V
+            -1.0f, -1.0f, 0, 0.f, 0.f,
+            1.0f, -1.0f, 0, 1.f, 0.f,
+            -1.0f,  1.0f, 0, 0.f, 1.f,
+            1.0f,  1.0f, 0, 1.f, 1.f,
+        };
+
+        private FloatBuffer mTriangleVertices;
+
+        private final String mVertexShader =
+                "uniform mat4 uMVPMatrix;\n" +
+                "uniform mat4 uSTMatrix;\n" +
+                "attribute vec4 aPosition;\n" +
+                "attribute vec4 aTextureCoord;\n" +
+                "varying vec2 vTextureCoord;\n" +
+                "void main() {\n" +
+                "  gl_Position = uMVPMatrix * aPosition;\n" +
+                "  vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +
+                "}\n";
+
+        private final String mFragmentShader =
+                "#extension GL_OES_EGL_image_external : require\n" +
+                "precision mediump float;\n" +
+                "varying vec2 vTextureCoord;\n" +
+                "uniform samplerExternalOES sTexture;\n" +
+                "void main() {\n" +
+                "  gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
+                "}\n";
+
+        private float[] mMVPMatrix = new float[16];
+        private float[] mSTMatrix = new float[16];
+
+        private int mProgram;
+        private int mTextureID = -12345;
+        private int muMVPMatrixHandle;
+        private int muSTMatrixHandle;
+        private int maPositionHandle;
+        private int maTextureHandle;
+
+        public VideoRender() {
+            mTriangleVertices = ByteBuffer.allocateDirect(
+                mTriangleVerticesData.length * FLOAT_SIZE_BYTES)
+                    .order(ByteOrder.nativeOrder()).asFloatBuffer();
+            mTriangleVertices.put(mTriangleVerticesData).position(0);
+
+            Matrix.setIdentityM(mSTMatrix, 0);
+        }
+
+        public int getTextureId() {
+            return mTextureID;
+        }
+
+        public void onDrawFrame(SurfaceTexture st) {
+            checkGlError("onDrawFrame start");
+            st.getTransformMatrix(mSTMatrix);
+
+            GLES20.glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+            GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
+
+            GLES20.glUseProgram(mProgram);
+            checkGlError("glUseProgram");
+
+            GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
+            GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID);
+
+            mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
+            GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false,
+                TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
+            checkGlError("glVertexAttribPointer maPosition");
+            GLES20.glEnableVertexAttribArray(maPositionHandle);
+            checkGlError("glEnableVertexAttribArray maPositionHandle");
+
+            mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
+            GLES20.glVertexAttribPointer(maTextureHandle, 3, GLES20.GL_FLOAT, false,
+                TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
+            checkGlError("glVertexAttribPointer maTextureHandle");
+            GLES20.glEnableVertexAttribArray(maTextureHandle);
+            checkGlError("glEnableVertexAttribArray maTextureHandle");
+
+            Matrix.setIdentityM(mMVPMatrix, 0);
+            GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
+            GLES20.glUniformMatrix4fv(muSTMatrixHandle, 1, false, mSTMatrix, 0);
+
+            GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
+            checkGlError("glDrawArrays");
+            GLES20.glFinish();
+        }
+
+        public void onSurfaceCreated() {
+            mProgram = createProgram(mVertexShader, mFragmentShader);
+            if (mProgram == 0) {
+                Log.e(TAG, "failed creating program");
+                return;
+            }
+            maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
+            checkGlError("glGetAttribLocation aPosition");
+            if (maPositionHandle == -1) {
+                throw new RuntimeException("Could not get attrib location for aPosition");
+            }
+            maTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord");
+            checkGlError("glGetAttribLocation aTextureCoord");
+            if (maTextureHandle == -1) {
+                throw new RuntimeException("Could not get attrib location for aTextureCoord");
+            }
+
+            muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
+            checkGlError("glGetUniformLocation uMVPMatrix");
+            if (muMVPMatrixHandle == -1) {
+                throw new RuntimeException("Could not get attrib location for uMVPMatrix");
+            }
+
+            muSTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uSTMatrix");
+            checkGlError("glGetUniformLocation uSTMatrix");
+            if (muSTMatrixHandle == -1) {
+                throw new RuntimeException("Could not get attrib location for uSTMatrix");
+            }
+
+
+            int[] textures = new int[1];
+            GLES20.glGenTextures(1, textures, 0);
+
+            mTextureID = textures[0];
+            GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID);
+            checkGlError("glBindTexture mTextureID");
+
+            GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,
+                    GLES20.GL_NEAREST);
+            GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,
+                    GLES20.GL_LINEAR);
+            GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S,
+                    GLES20.GL_CLAMP_TO_EDGE);
+            GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T,
+                    GLES20.GL_CLAMP_TO_EDGE);
+            checkGlError("glTexParameter");
+        }
+
+        private int loadShader(int shaderType, String source) {
+            int shader = GLES20.glCreateShader(shaderType);
+            checkGlError("glCreateShader type=" + shaderType);
+            GLES20.glShaderSource(shader, source);
+            GLES20.glCompileShader(shader);
+            int[] compiled = new int[1];
+            GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
+            if (compiled[0] == 0) {
+                Log.e(TAG, "Could not compile shader " + shaderType + ":");
+                Log.e(TAG, GLES20.glGetShaderInfoLog(shader));
+                GLES20.glDeleteShader(shader);
+                shader = 0;
+            }
+            return shader;
+        }
+
+        private int createProgram(String vertexSource, String fragmentSource) {
+            int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
+            if (vertexShader == 0) {
+                return 0;
+            }
+            int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
+            if (pixelShader == 0) {
+                return 0;
+            }
+
+            int program = GLES20.glCreateProgram();
+            checkGlError("glCreateProgram");
+            if (program == 0) {
+                Log.e(TAG, "Could not create program");
+            }
+            GLES20.glAttachShader(program, vertexShader);
+            checkGlError("glAttachShader");
+            GLES20.glAttachShader(program, pixelShader);
+            checkGlError("glAttachShader");
+            GLES20.glLinkProgram(program);
+            int[] linkStatus = new int[1];
+            GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
+            if (linkStatus[0] != GLES20.GL_TRUE) {
+                Log.e(TAG, "Could not link program: ");
+                Log.e(TAG, GLES20.glGetProgramInfoLog(program));
+                GLES20.glDeleteProgram(program);
+                program = 0;
+            }
+            return program;
+        }
+
+        public void checkGlError(String op) {
+            int error;
+            while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
+                Log.e(TAG, op + ": glError " + error);
+                throw new RuntimeException(op + ": glError " + error);
+            }
+        }
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
index dda78f8..c6d2985 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
@@ -750,7 +750,7 @@
             return;
         }
         for (int i = 0; i < trackInfos.length; ++i) {
-            if (trackInfos[i] == null) continue;
+            assertTrue(trackInfos[i] != null);
             if (trackInfos[i].getTrackType() ==
                  MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT) {
                 mTimedTextTrackIndex.add(i);
@@ -907,6 +907,35 @@
         mMediaPlayer.stop();
     }
 
+    public void testGetTrackInfo() throws Exception {
+        loadResource(R.raw.testvideo_with_2_subtitles);
+        loadSubtitleSource(R.raw.test_subtitle1_srt);
+        loadSubtitleSource(R.raw.test_subtitle2_srt);
+        mMediaPlayer.prepare();
+        mMediaPlayer.start();
+
+        readTimedTextTracks();
+        selectSubtitleTrack(2);
+
+        int count = 0;
+        MediaPlayer.TrackInfo[] trackInfos = mMediaPlayer.getTrackInfo();
+        assertTrue(trackInfos != null && trackInfos.length != 0);
+        for (int i = 0; i < trackInfos.length; ++i) {
+            assertTrue(trackInfos[i] != null);
+            if (trackInfos[i].getTrackType() == MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT) {
+                String trackLanguage = trackInfos[i].getLanguage();
+                assertTrue(trackLanguage != null);
+                trackLanguage.trim();
+                Log.d(LOG_TAG, "track info lang: " + trackLanguage);
+                assertTrue("Should not see empty track language with our test data.",
+                           trackLanguage.length() > 0);
+                count++;
+            }
+        }
+        // There are 4 subtitle tracks in total in our test data.
+        assertEquals(4, count);
+    }
+
     public void testCallback() throws Throwable {
         final int mp4Duration = 8484;
 
diff --git a/tests/tests/nativeopengl/Android.mk b/tests/tests/nativeopengl/Android.mk
new file mode 100644
index 0000000..dd19548
--- /dev/null
+++ b/tests/tests/nativeopengl/Android.mk
@@ -0,0 +1,41 @@
+# Copyright 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsNativeOpenGLTestCases
+
+# Don't include this package in any target.
+LOCAL_MODULE_TAGS := optional
+
+# When built, explicitly put it in the data partition.
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+# All tests should include android.test.runner.
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner ctswrappedgtest
+
+LOCAL_JNI_SHARED_LIBRARIES := libnativeopengltests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_GTEST_PACKAGE)
+
+# Include the associated library's makefile.
+include $(LOCAL_PATH)/libnativeopengltests/Android.mk
diff --git a/tests/tests/nativeopengl/AndroidManifest.xml b/tests/tests/nativeopengl/AndroidManifest.xml
new file mode 100644
index 0000000..52157b4
--- /dev/null
+++ b/tests/tests/nativeopengl/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * 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.
+ */
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.opengl.cts">
+
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:name="com.android.opengl.cts.GLTestActivity"/>
+    </application>
+
+    <!-- This is a self-instrumenting test package. -->
+    <instrumentation android:name="GLTestInstrumentation"
+                     android:targetPackage="com.android.opengl.cts"
+                     android:label="Native OpenGL tests">
+    </instrumentation>
+</manifest>
diff --git a/tests/tests/nativeopengl/libnativeopengltests/Android.mk b/tests/tests/nativeopengl/libnativeopengltests/Android.mk
new file mode 100644
index 0000000..b6ca1cb
--- /dev/null
+++ b/tests/tests/nativeopengl/libnativeopengltests/Android.mk
@@ -0,0 +1,48 @@
+# Copyright 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# This is the shared library included by the JNI test app.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libnativeopengltests
+
+# Don't include this package in any configuration by default.
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_C_INCLUDES := $(JNI_H_INCLUDE) \
+    bionic \
+    bionic/libstdc++/include \
+    external/gtest/include \
+    external/stlport/stlport
+
+LOCAL_SRC_FILES := \
+        register.cpp \
+        GLTestHelper.cpp \
+        android_test_wrappedgtest_WrappedGTestActivity.cpp \
+        com_android_opengl_cts_GLTestActivity.cpp \
+        tests/GLTest_test.cpp
+
+LOCAL_SHARED_LIBRARIES := libEGL \
+                          libGLESv2 \
+                          libstlport \
+                          libandroid
+
+LOCAL_STATIC_LIBRARIES := libgtest
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/src/android/view/inputmethod/cts/InputMethodInfoStub.java b/tests/tests/nativeopengl/libnativeopengltests/GLTestHelper.cpp
similarity index 66%
copy from tests/src/android/view/inputmethod/cts/InputMethodInfoStub.java
copy to tests/tests/nativeopengl/libnativeopengltests/GLTestHelper.cpp
index 0eeefbe..ed527a6 100644
--- a/tests/src/android/view/inputmethod/cts/InputMethodInfoStub.java
+++ b/tests/tests/nativeopengl/libnativeopengltests/GLTestHelper.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright 2012 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,10 +14,16 @@
  * limitations under the License.
  */
 
-package android.view.inputmethod.cts;
+#include "GLTestHelper.h"
 
-import android.inputmethodservice.InputMethodService;
+using namespace android;
 
-public class InputMethodInfoStub extends InputMethodService {
+ANativeWindow* GLTestHelper::mWindow;
 
+ANativeWindow* GLTestHelper::getWindow() {
+    return mWindow;
+}
+
+void GLTestHelper::setWindow(ANativeWindow* value) {
+    mWindow = value;
 }
diff --git a/tests/tests/nativeopengl/libnativeopengltests/GLTestHelper.h b/tests/tests/nativeopengl/libnativeopengltests/GLTestHelper.h
new file mode 100644
index 0000000..c0775f0
--- /dev/null
+++ b/tests/tests/nativeopengl/libnativeopengltests/GLTestHelper.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_GL_TEST_HELPER_H
+#define ANDROID_GL_TEST_HELPER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <android/native_window.h>
+#include <utils/RefBase.h>
+
+using namespace android;
+
+class GLTestHelper
+{
+private:
+    static ANativeWindow* mWindow;
+public:
+    static ANativeWindow* getWindow();
+    static void setWindow(ANativeWindow* value);
+};
+
+
+
+#endif // ANDROID_GL_TEST_HELPER_H
diff --git a/tests/tests/nativeopengl/libnativeopengltests/android_test_wrappedgtest_WrappedGTestActivity.cpp b/tests/tests/nativeopengl/libnativeopengltests/android_test_wrappedgtest_WrappedGTestActivity.cpp
new file mode 100644
index 0000000..5e1c30e
--- /dev/null
+++ b/tests/tests/nativeopengl/libnativeopengltests/android_test_wrappedgtest_WrappedGTestActivity.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <android/log.h>
+#include <gtest/gtest.h>
+#include <jni.h>
+
+using namespace testing;
+
+class GTestListener : public EmptyTestEventListener {
+public:
+    GTestListener(JNIEnv *env, jobject activity)
+        : mActivity(activity), mEnv(env) {
+
+        jclass clazz = env->FindClass(
+              "android/test/wrappedgtest/WrappedGTestActivity");
+        mSendStatusID = env->GetMethodID(clazz, "sendStatus",
+              "(Ljava/lang/String;)V");
+        mMessageBuffer = new char[2048];
+    }
+
+    ~GTestListener() {
+        delete[] mMessageBuffer;
+    }
+
+private:
+    jobject   mActivity;
+    JNIEnv *  mEnv;
+    jmethodID mSendStatusID;
+    char *    mMessageBuffer;
+
+    virtual void OnTestIterationStart(const UnitTest& unit_test,
+            int iteration) {
+        snprintf(mMessageBuffer, sizeof(char) * 2048,
+                "[==========] Running %i tests from %i test cases.",
+                unit_test.test_to_run_count(),
+                unit_test.test_case_to_run_count());
+
+        mEnv->CallVoidMethod(mActivity, mSendStatusID,
+                mEnv->NewStringUTF(mMessageBuffer));
+    }
+
+    virtual void OnTestStart(const TestInfo& test_info) {
+        snprintf(mMessageBuffer, sizeof(char) * 2048, "[ RUN      ] %s.%s",
+                test_info.test_case_name(), test_info.name());
+
+        mEnv->CallVoidMethod(mActivity, mSendStatusID,
+                mEnv->NewStringUTF(mMessageBuffer));
+    }
+
+    virtual void OnTestPartResult(const TestPartResult& result) {
+        if (result.type() == TestPartResult::kSuccess) {
+            return;
+        }
+
+        snprintf(mMessageBuffer, sizeof(char) * 2048, "%s:%i: Failure\n%s",
+                result.file_name(), result.line_number(), result.message());
+
+        mEnv->CallVoidMethod(mActivity, mSendStatusID,
+                mEnv->NewStringUTF(mMessageBuffer));
+    }
+
+    virtual void OnTestEnd(const TestInfo& test_info) {
+        const char * result = test_info.result()->Passed() ?
+                "[       OK ] " : "[  FAILED  ] ";
+
+        snprintf(mMessageBuffer, sizeof(char) * 2048, "%s%s.%s (%lli ms)",
+                result, test_info.test_case_name(), test_info.name(),
+                test_info.result()->elapsed_time());
+
+        mEnv->CallVoidMethod(mActivity, mSendStatusID,
+                mEnv->NewStringUTF(mMessageBuffer));
+    }
+
+    virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration) {
+        snprintf(mMessageBuffer, sizeof(char) * 2048,
+                "[==========] %i tests from %i test cases ran. (%lli ms total)",
+                unit_test.test_to_run_count(),
+                unit_test.test_case_to_run_count(), unit_test.elapsed_time());
+
+        mEnv->CallVoidMethod(mActivity, mSendStatusID,
+                mEnv->NewStringUTF(mMessageBuffer));
+    }
+};
+
+static jboolean WrappedGTestActivity_runTests(JNIEnv *env, jobject obj,
+        jobject activity) {
+    // init gtest with no args
+    int argc = 0;
+    InitGoogleTest(&argc, (char**)NULL);
+
+    // delete the default listener
+    TestEventListeners& listeners = UnitTest::GetInstance()->listeners();
+    delete listeners.Release(listeners.default_result_printer());
+
+    // add custom listener
+    GTestListener * listener = new GTestListener(env, activity);
+    listeners.Append(listener);
+
+    // run tests
+    int result = RUN_ALL_TESTS();
+
+    delete listener;
+    return result;
+};
+
+static JNINativeMethod methods[] = {
+    // name, signature, function
+    { "runTests", "(Landroid/test/wrappedgtest/WrappedGTestActivity;)I", (void*)WrappedGTestActivity_runTests },
+};
+
+int register_WrappedGTestActivity(JNIEnv *env) {
+    return env->RegisterNatives(
+            env->FindClass("android/test/wrappedgtest/WrappedGTestActivity"),
+            methods, sizeof(methods) / sizeof(JNINativeMethod));
+};
diff --git a/tests/tests/nativeopengl/libnativeopengltests/com_android_opengl_cts_GLTestActivity.cpp b/tests/tests/nativeopengl/libnativeopengltests/com_android_opengl_cts_GLTestActivity.cpp
new file mode 100644
index 0000000..8544f2a
--- /dev/null
+++ b/tests/tests/nativeopengl/libnativeopengltests/com_android_opengl_cts_GLTestActivity.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <jni.h>
+
+#include "GLTestHelper.h"
+#include <android/native_window_jni.h>
+
+using namespace android;
+
+static void GLTestActivity_setSurface(JNIEnv *env, jobject obj,
+        jobject surface) {
+    ANativeWindow* window = ANativeWindow_fromSurface(env, surface);
+    GLTestHelper::setWindow(window);
+};
+
+static JNINativeMethod methods[] = {
+    // name, signature, function
+    { "setSurface", "(Landroid/view/Surface;)V", (void*)GLTestActivity_setSurface },
+};
+
+int register_GLTestActivity(JNIEnv *env) {
+    return env->RegisterNatives(
+            env->FindClass("com/android/opengl/cts/GLTestActivity"),
+            methods, sizeof(methods) / sizeof(JNINativeMethod));
+};
diff --git a/tests/tests/nativeopengl/libnativeopengltests/register.cpp b/tests/tests/nativeopengl/libnativeopengltests/register.cpp
new file mode 100644
index 0000000..97a8bd4
--- /dev/null
+++ b/tests/tests/nativeopengl/libnativeopengltests/register.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jni.h>
+#include <stdlib.h>
+
+/*
+ * This function is called automatically by the system when this
+ * library is loaded. We use it to register all our native functions,
+ * which is the recommended practice for Android.
+ */
+jint JNI_OnLoad(JavaVM *vm, void *reserved) {
+    JNIEnv *env = NULL;
+
+    if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) {
+        return JNI_ERR;
+    }
+
+    extern int register_WrappedGTestActivity(JNIEnv *);
+    if (register_WrappedGTestActivity(env)) {
+        return JNI_ERR;
+    }
+
+    extern int register_GLTestActivity(JNIEnv *);
+    if (register_GLTestActivity(env)) {
+        return JNI_ERR;
+    }
+
+    return JNI_VERSION_1_4;
+}
diff --git a/tests/tests/nativeopengl/libnativeopengltests/tests/GLTest_test.cpp b/tests/tests/nativeopengl/libnativeopengltests/tests/GLTest_test.cpp
new file mode 100644
index 0000000..37bfcb5
--- /dev/null
+++ b/tests/tests/nativeopengl/libnativeopengltests/tests/GLTest_test.cpp
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <android/native_window.h>
+
+#include <gtest/gtest.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+
+#include "GLTestHelper.h"
+
+
+namespace android {
+
+class GLTest : public ::testing::Test {
+
+protected:
+
+    GLTest():
+            mEglDisplay(EGL_NO_DISPLAY),
+            mEglSurface(EGL_NO_SURFACE),
+            mEglContext(EGL_NO_CONTEXT) {
+    }
+
+
+   virtual void SetUp() {
+        mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay);
+
+        EGLint majorVersion;
+        EGLint minorVersion;
+        EXPECT_TRUE(eglInitialize(mEglDisplay, &majorVersion, &minorVersion));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+        EGLint numConfigs = 0;
+        EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &mGlConfig,
+                1, &numConfigs));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+        char* displaySecsEnv = getenv("GLTEST_DISPLAY_SECS");
+        if (displaySecsEnv != NULL) {
+            mDisplaySecs = atoi(displaySecsEnv);
+            if (mDisplaySecs < 0) {
+                mDisplaySecs = 0;
+            }
+        } else {
+            mDisplaySecs = 0;
+        }
+
+        if (mDisplaySecs > 0) {
+            mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
+                    GLTestHelper::getWindow(), NULL);
+        } else {
+            EGLint pbufferAttribs[] = {
+                EGL_WIDTH, getSurfaceWidth(),
+                EGL_HEIGHT, getSurfaceHeight(),
+                EGL_NONE };
+
+            mEglSurface = eglCreatePbufferSurface(mEglDisplay, mGlConfig,
+                    pbufferAttribs);
+        }
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        ASSERT_NE(EGL_NO_SURFACE, mEglSurface);
+
+        mEglContext = eglCreateContext(mEglDisplay, mGlConfig, EGL_NO_CONTEXT,
+                getContextAttribs());
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        ASSERT_NE(EGL_NO_CONTEXT, mEglContext);
+
+        EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
+                mEglContext));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+        EGLint w, h;
+        EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &w));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &h));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+        glViewport(0, 0, w, h);
+        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+    }
+
+    virtual void TearDown() {
+        // Display the result
+        if (mDisplaySecs > 0 && mEglSurface != EGL_NO_SURFACE) {
+            eglSwapBuffers(mEglDisplay, mEglSurface);
+            sleep(mDisplaySecs);
+        }
+
+        if (mEglContext != EGL_NO_CONTEXT) {
+            eglDestroyContext(mEglDisplay, mEglContext);
+        }
+        if (mEglSurface != EGL_NO_SURFACE) {
+            eglDestroySurface(mEglDisplay, mEglSurface);
+        }
+        if (mEglDisplay != EGL_NO_DISPLAY) {
+            eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
+                    EGL_NO_CONTEXT);
+            eglTerminate(mEglDisplay);
+        }
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+    }
+
+    virtual EGLint const* getConfigAttribs() {
+        static EGLint sDefaultConfigAttribs[] = {
+            EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+            EGL_RED_SIZE, 8,
+            EGL_GREEN_SIZE, 8,
+            EGL_BLUE_SIZE, 8,
+            EGL_ALPHA_SIZE, 8,
+            EGL_DEPTH_SIZE, 16,
+            EGL_STENCIL_SIZE, 8,
+            EGL_NONE };
+
+        return sDefaultConfigAttribs;
+    }
+
+    virtual EGLint const* getContextAttribs() {
+        static EGLint sDefaultContextAttribs[] = {
+            EGL_CONTEXT_CLIENT_VERSION, 2,
+            EGL_NONE };
+
+        return sDefaultContextAttribs;
+    }
+
+    virtual EGLint getSurfaceWidth() {
+        return 512;
+    }
+
+    virtual EGLint getSurfaceHeight() {
+        return 512;
+    }
+
+    bool checkPixel(GLubyte * actual, GLubyte * expected, int tolerance) {
+        for (int i = 0; i < 4; i++) {
+            if (abs(actual[i] - expected[i]) > tolerance) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    ::testing::AssertionResult AssertPixel(const char* a_expr,
+            const char* e_expr, const char* t_expr, GLubyte * actual,
+            GLubyte * expected, int tolerance) {
+
+        if (checkPixel(actual, expected, tolerance)) {
+            return ::testing::AssertionSuccess();
+        }
+
+        return ::testing::AssertionFailure()
+            << "Pixel comparison failed with tolerance " << tolerance << "\n"
+            << "Actual: r=" << (int)actual[0] << " g=" << (int)actual[1]
+            << " b=" << (int)actual[2] << " a=" << (int)actual[3] << "\n"
+            << "Expected: r=" << (int)expected[0] << " g=" << (int)expected[1]
+            << " b=" << (int)expected[2] << " a=" << (int)expected[3] << "\n";
+    }
+
+    int mDisplaySecs;
+
+    EGLDisplay mEglDisplay;
+    EGLSurface mEglSurface;
+    EGLContext mEglContext;
+    EGLConfig  mGlConfig;
+};
+
+TEST_F(GLTest, ClearColorTest) {
+    glClearColor(0.2, 0.2, 0.2, 0.2);
+    glClear(GL_COLOR_BUFFER_BIT);
+    GLubyte expected[4] = { 51, 51, 51, 51 };
+    GLubyte pixel[4];
+    glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
+    ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+    ASSERT_PRED_FORMAT3(AssertPixel, pixel, expected, 2);
+}
+
+} // namespace android
diff --git a/tests/tests/nativeopengl/src/com/android/opengl/cts/GLTestActivity.java b/tests/tests/nativeopengl/src/com/android/opengl/cts/GLTestActivity.java
new file mode 100644
index 0000000..1633a93
--- /dev/null
+++ b/tests/tests/nativeopengl/src/com/android/opengl/cts/GLTestActivity.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.opengl.cts;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.Intent;
+import android.os.Bundle;
+import android.test.wrappedgtest.WrappedGTestActivity;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.Surface;
+
+public class GLTestActivity extends WrappedGTestActivity {
+
+    private SurfaceView mSurfaceView;
+    private SurfaceHolder.Callback mHolderCallback = new SurfaceHolder.Callback() {
+
+        @Override
+        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+             setSurface(holder.getSurface());
+        }
+
+        @Override
+        public void surfaceCreated(SurfaceHolder holder) {
+             setSurface(holder.getSurface());
+        }
+
+        @Override
+        public void surfaceDestroyed(SurfaceHolder holder) {
+        }
+    };
+
+    public void onCreate(Bundle data) {
+        super.onCreate(data);
+        mSurfaceView = new SurfaceView(this);
+        mSurfaceView.getHolder().addCallback(mHolderCallback);
+        setContentView(mSurfaceView);
+        System.loadLibrary("nativeopengltests");
+    }
+
+    private static native void setSurface(Surface surface);
+}
diff --git a/tests/tests/nativeopengl/src/com/android/opengl/cts/GLTestInstrumentation.java b/tests/tests/nativeopengl/src/com/android/opengl/cts/GLTestInstrumentation.java
new file mode 100644
index 0000000..913f0eb
--- /dev/null
+++ b/tests/tests/nativeopengl/src/com/android/opengl/cts/GLTestInstrumentation.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.opengl.cts;
+
+import android.test.wrappedgtest.WrappedGTestInstrumentation;
+
+/**
+ * adb shell am instrument -w com.android.opengl.cts/.GLTestInstrumentation
+ */
+public class GLTestInstrumentation extends WrappedGTestInstrumentation {
+    public GLTestInstrumentation() {
+        mActivityClass = GLTestActivity.class;
+    }
+}
diff --git a/tests/tests/net/Android.mk b/tests/tests/net/Android.mk
index b327392..f69c4c3 100644
--- a/tests/tests/net/Android.mk
+++ b/tests/tests/net/Android.mk
@@ -21,7 +21,7 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner voip-common
 
 # include CtsTestServer as a temporary hack to free net.cts from cts.stub.
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/openglperf/Android.mk b/tests/tests/openglperf/Android.mk
index 2d1767e..e10d1c7 100644
--- a/tests/tests/openglperf/Android.mk
+++ b/tests/tests/openglperf/Android.mk
@@ -23,7 +23,9 @@
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner ctsutil
+
+LOCAL_JNI_SHARED_LIBRARIES := libctsopenglperf_jni
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
@@ -35,4 +37,4 @@
 
 include $(BUILD_CTS_PACKAGE)
 
-#include $(call all-makefiles-under,$(LOCAL_PATH))
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/openglperf/AndroidManifest.xml b/tests/tests/openglperf/AndroidManifest.xml
index 540f190..84bfea4 100644
--- a/tests/tests/openglperf/AndroidManifest.xml
+++ b/tests/tests/openglperf/AndroidManifest.xml
@@ -32,12 +32,9 @@
         android:targetPackage="com.android.cts.openglperf"
         android:name="android.test.InstrumentationCtsTestRunner" />
 
-    <application
-        android:label="@string/app_name" >
+    <application>
         <uses-library android:name="android.test.runner" />
-        <activity
-            android:name="android.openglperf.cts.GlPlanetsActivity"
-            android:label="@string/app_name" >
+        <activity android:name="android.openglperf.cts.GlPlanetsActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
diff --git a/suite/pts/hostTests/ptshostutil/Android.mk b/tests/tests/openglperf/jni/Android.mk
similarity index 67%
copy from suite/pts/hostTests/ptshostutil/Android.mk
copy to tests/tests/openglperf/jni/Android.mk
index 14e786a..77db1df 100644
--- a/suite/pts/hostTests/ptshostutil/Android.mk
+++ b/tests/tests/openglperf/jni/Android.mk
@@ -11,19 +11,21 @@
 # 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.
-
+#
 LOCAL_PATH := $(call my-dir)
 
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed-prebuilt ddmlib-prebuilt junit
+LOCAL_MODULE    := libctsopenglperf_jni
 
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_MODULE := ptshostutil
+LOCAL_SRC_FILES := OpenGlPerfNativeJni.cpp
 
-include $(BUILD_HOST_JAVA_LIBRARY)
+LOCAL_C_INCLUDES := $(JNI_H_INCLUDE) $(call include-path-for, system-core) frameworks/native/opengl/include
 
+LOCAL_SHARED_LIBRARIES := libnativehelper libcutils libGLES_trace libEGL
+
+LOCAL_SDK_VERSION := 14
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/tests/openglperf/jni/OpenGlPerfNativeJni.cpp b/tests/tests/openglperf/jni/OpenGlPerfNativeJni.cpp
new file mode 100644
index 0000000..63311eb
--- /dev/null
+++ b/tests/tests/openglperf/jni/OpenGlPerfNativeJni.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <EGL/egl.h>
+#define EGL_EGLEXT_PROTOTYPES // for egl*Sync*
+#include <EGL/eglext.h>
+#include <cutils/log.h>
+#include <jni.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+typedef EGLSyncKHR EGLAPIENTRY (*TypeEglCreateSyncKHR)(EGLDisplay dpy, \
+    EGLenum type, const EGLint *attrib_list);
+typedef EGLBoolean EGLAPIENTRY (*TypeEglDestroySyncKHR)(EGLDisplay dpy, \
+    EGLSyncKHR sync);
+typedef EGLint EGLAPIENTRY (*TypeEglClientWaitSyncKHR)(EGLDisplay dpy, \
+    EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout);
+
+static TypeEglCreateSyncKHR mEglCreateSyncKHR = NULL;
+static TypeEglClientWaitSyncKHR mEglClientWaitSyncKHR = NULL;
+static TypeEglDestroySyncKHR mEglDestroySyncKHR = NULL;
+static bool mInitialized = false;
+static bool mEglKhrFenceSyncSupported = false;
+
+bool IsEglKHRFenceSyncSupported(EGLDisplay& display)
+{
+    if (!mInitialized) {
+        const char* eglExtensions = eglQueryString(display, EGL_EXTENSIONS);
+        if (eglExtensions && strstr(eglExtensions, "EGL_KHR_fence_sync")) {
+            mEglCreateSyncKHR = (TypeEglCreateSyncKHR) eglGetProcAddress("eglCreateSyncKHR");
+            mEglClientWaitSyncKHR =
+                    (TypeEglClientWaitSyncKHR) eglGetProcAddress("eglClientWaitSyncKHR");
+            mEglDestroySyncKHR = (TypeEglDestroySyncKHR) eglGetProcAddress("eglDestroySyncKHR");
+            if (mEglCreateSyncKHR != NULL && mEglClientWaitSyncKHR != NULL
+                    && mEglDestroySyncKHR != NULL) {
+                mEglKhrFenceSyncSupported = true;
+            }
+        }
+        mInitialized = true;
+    }
+    return mEglKhrFenceSyncSupported;
+}
+
+extern "C" JNIEXPORT \
+jboolean JNICALL Java_android_openglperf_cts_OpenGlPerfNative_waitForEglCompletion(JNIEnv* env,
+        jclass clazz, jlong waitTimeInNs)
+{
+    EGLDisplay dpy = eglGetCurrentDisplay();
+    if (!IsEglKHRFenceSyncSupported(dpy)) {
+        return JNI_FALSE;
+    }
+    EGLSyncKHR sync = mEglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL);
+    if (sync == EGL_NO_SYNC_KHR) {
+        return JNI_FALSE;
+    }
+    jboolean res = JNI_TRUE;
+    EGLint result = mEglClientWaitSyncKHR(dpy, sync, 0, waitTimeInNs);
+    if (result == EGL_FALSE) {
+        ALOGE("FrameCompletion: error waiting for fence: %#x", eglGetError());
+        res = JNI_FALSE;
+    } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+        ALOGE("FrameCompletion: timeout waiting for fence");
+        res = JNI_FALSE;
+    }
+    mEglDestroySyncKHR(dpy, sync);
+    return res;
+}
diff --git a/tests/tests/openglperf/res/values/strings.xml b/tests/tests/openglperf/res/values/strings.xml
deleted file mode 100644
index 2cdba7f..0000000
--- a/tests/tests/openglperf/res/values/strings.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources>
-    <string name="app_name">OpenGlPerfTest</string>
-</resources>
\ No newline at end of file
diff --git a/tests/tests/openglperf/src/android/openglperf/cts/GLSurfaceViewCustom.java b/tests/tests/openglperf/src/android/openglperf/cts/GLSurfaceViewCustom.java
new file mode 100644
index 0000000..e1a2292
--- /dev/null
+++ b/tests/tests/openglperf/src/android/openglperf/cts/GLSurfaceViewCustom.java
@@ -0,0 +1,1943 @@
+/*
+ * 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.openglperf.cts;
+
+// This is a copy of f/b/opengl/java/android/opengl/GlSurfaceView.
+// Most of the code is used as it is, but there was a need to add a hook
+// after eglSwapBuffers is called. Also classes not available outside framework is commented out
+// or replaced.
+
+
+import java.io.Writer;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGL11;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.egl.EGLSurface;
+import javax.microedition.khronos.opengles.GL;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+/**
+ * An implementation of SurfaceView that uses the dedicated surface for
+ * displaying OpenGL rendering.
+ * <p>
+ * A GLSurfaceView provides the following features:
+ * <p>
+ * <ul>
+ * <li>Manages a surface, which is a special piece of memory that can be
+ * composited into the Android view system.
+ * <li>Manages an EGL display, which enables OpenGL to render into a surface.
+ * <li>Accepts a user-provided Renderer object that does the actual rendering.
+ * <li>Renders on a dedicated thread to decouple rendering performance from the
+ * UI thread.
+ * <li>Supports both on-demand and continuous rendering.
+ * <li>Optionally wraps, traces, and/or error-checks the renderer's OpenGL calls.
+ * </ul>
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For more information about how to use OpenGL, read the
+ * <a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL</a> developer guide.</p>
+ * </div>
+ *
+ * <h3>Using GLSurfaceView</h3>
+ * <p>
+ * Typically you use GLSurfaceView by subclassing it and overriding one or more of the
+ * View system input event methods. If your application does not need to override event
+ * methods then GLSurfaceView can be used as-is. For the most part
+ * GLSurfaceView behavior is customized by calling "set" methods rather than by subclassing.
+ * For example, unlike a regular View, drawing is delegated to a separate Renderer object which
+ * is registered with the GLSurfaceView
+ * using the {@link #setRenderer(Renderer)} call.
+ * <p>
+ * <h3>Initializing GLSurfaceView</h3>
+ * All you have to do to initialize a GLSurfaceView is call {@link #setRenderer(Renderer)}.
+ * However, if desired, you can modify the default behavior of GLSurfaceView by calling one or
+ * more of these methods before calling setRenderer:
+ * <ul>
+ * <li>{@link #setDebugFlags(int)}
+ * <li>{@link #setEGLConfigChooser(boolean)}
+ * <li>{@link #setEGLConfigChooser(EGLConfigChooser)}
+ * <li>{@link #setEGLConfigChooser(int, int, int, int, int, int)}
+ * <li>{@link #setGLWrapper(GLWrapper)}
+ * </ul>
+ * <p>
+ * <h4>Specifying the android.view.Surface</h4>
+ * By default GLSurfaceView will create a PixelFormat.RGB_888 format surface. If a translucent
+ * surface is required, call getHolder().setFormat(PixelFormat.TRANSLUCENT).
+ * The exact format of a TRANSLUCENT surface is device dependent, but it will be
+ * a 32-bit-per-pixel surface with 8 bits per component.
+ * <p>
+ * <h4>Choosing an EGL Configuration</h4>
+ * A given Android device may support multiple EGLConfig rendering configurations.
+ * The available configurations may differ in how may channels of data are present, as
+ * well as how many bits are allocated to each channel. Therefore, the first thing
+ * GLSurfaceView has to do when starting to render is choose what EGLConfig to use.
+ * <p>
+ * By default GLSurfaceView chooses a EGLConfig that has an RGB_888 pixel format,
+ * with at least a 16-bit depth buffer and no stencil.
+ * <p>
+ * If you would prefer a different EGLConfig
+ * you can override the default behavior by calling one of the
+ * setEGLConfigChooser methods.
+ * <p>
+ * <h4>Debug Behavior</h4>
+ * You can optionally modify the behavior of GLSurfaceView by calling
+ * one or more of the debugging methods {@link #setDebugFlags(int)},
+ * and {@link #setGLWrapper}. These methods may be called before and/or after setRenderer, but
+ * typically they are called before setRenderer so that they take effect immediately.
+ * <p>
+ * <h4>Setting a Renderer</h4>
+ * Finally, you must call {@link #setRenderer} to register a {@link Renderer}.
+ * The renderer is
+ * responsible for doing the actual OpenGL rendering.
+ * <p>
+ * <h3>Rendering Mode</h3>
+ * Once the renderer is set, you can control whether the renderer draws
+ * continuously or on-demand by calling
+ * {@link #setRenderMode}. The default is continuous rendering.
+ * <p>
+ * <h3>Activity Life-cycle</h3>
+ * A GLSurfaceView must be notified when the activity is paused and resumed. GLSurfaceView clients
+ * are required to call {@link #onPause()} when the activity pauses and
+ * {@link #onResume()} when the activity resumes. These calls allow GLSurfaceView to
+ * pause and resume the rendering thread, and also allow GLSurfaceView to release and recreate
+ * the OpenGL display.
+ * <p>
+ * <h3>Handling events</h3>
+ * <p>
+ * To handle an event you will typically subclass GLSurfaceView and override the
+ * appropriate method, just as you would with any other View. However, when handling
+ * the event, you may need to communicate with the Renderer object
+ * that's running in the rendering thread. You can do this using any
+ * standard Java cross-thread communication mechanism. In addition,
+ * one relatively easy way to communicate with your renderer is
+ * to call
+ * {@link #queueEvent(Runnable)}. For example:
+ * <pre class="prettyprint">
+ * class MyGLSurfaceView extends GLSurfaceView {
+ *
+ *     private MyRenderer mMyRenderer;
+ *
+ *     public void start() {
+ *         mMyRenderer = ...;
+ *         setRenderer(mMyRenderer);
+ *     }
+ *
+ *     public boolean onKeyDown(int keyCode, KeyEvent event) {
+ *         if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
+ *             queueEvent(new Runnable() {
+ *                 // This method will be called on the rendering
+ *                 // thread:
+ *                 public void run() {
+ *                     mMyRenderer.handleDpadCenter();
+ *                 }});
+ *             return true;
+ *         }
+ *         return super.onKeyDown(keyCode, event);
+ *     }
+ * }
+ * </pre>
+ *
+ */
+public class GLSurfaceViewCustom extends SurfaceView implements SurfaceHolder.Callback {
+    private final static String TAG = "GLSurfaceView";
+    private final static boolean LOG_ATTACH_DETACH = false;
+    private final static boolean LOG_THREADS = false;
+    private final static boolean LOG_PAUSE_RESUME = false;
+    private final static boolean LOG_SURFACE = false;
+    private final static boolean LOG_RENDERER = false;
+    private final static boolean LOG_RENDERER_DRAW_FRAME = false;
+    private final static boolean LOG_EGL = false;
+    /**
+     * The renderer only renders
+     * when the surface is created, or when {@link #requestRender} is called.
+     *
+     * @see #getRenderMode()
+     * @see #setRenderMode(int)
+     * @see #requestRender()
+     */
+    public final static int RENDERMODE_WHEN_DIRTY = 0;
+    /**
+     * The renderer is called
+     * continuously to re-render the scene.
+     *
+     * @see #getRenderMode()
+     * @see #setRenderMode(int)
+     */
+    public final static int RENDERMODE_CONTINUOUSLY = 1;
+
+    /**
+     * Check glError() after every GL call and throw an exception if glError indicates
+     * that an error has occurred. This can be used to help track down which OpenGL ES call
+     * is causing an error.
+     *
+     * @see #getDebugFlags
+     * @see #setDebugFlags
+     */
+    public final static int DEBUG_CHECK_GL_ERROR = 1;
+
+    /**
+     * Log GL calls to the system log at "verbose" level with tag "GLSurfaceView".
+     *
+     * @see #getDebugFlags
+     * @see #setDebugFlags
+     */
+    public final static int DEBUG_LOG_GL_CALLS = 2;
+
+    /**
+     * Standard View constructor. In order to render something, you
+     * must call {@link #setRenderer} to register a renderer.
+     */
+    public GLSurfaceViewCustom(Context context) {
+        super(context);
+        init();
+    }
+
+    /**
+     * Standard View constructor. In order to render something, you
+     * must call {@link #setRenderer} to register a renderer.
+     */
+    public GLSurfaceViewCustom(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            if (mGLThread != null) {
+                // GLThread may still be running if this view was never
+                // attached to a window.
+                mGLThread.requestExitAndWait();
+            }
+        } finally {
+            super.finalize();
+        }
+    }
+
+    private void init() {
+        // Install a SurfaceHolder.Callback so we get notified when the
+        // underlying surface is created and destroyed
+        SurfaceHolder holder = getHolder();
+        holder.addCallback(this);
+        // setFormat is done by SurfaceView in SDK 2.3 and newer. Uncomment
+        // this statement if back-porting to 2.2 or older:
+        // holder.setFormat(PixelFormat.RGB_565);
+        //
+        // setType is not needed for SDK 2.0 or newer. Uncomment this
+        // statement if back-porting this code to older SDKs.
+        // holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
+    }
+
+    /**
+     * Set the glWrapper. If the glWrapper is not null, its
+     * {@link GLWrapper#wrap(GL)} method is called
+     * whenever a surface is created. A GLWrapper can be used to wrap
+     * the GL object that's passed to the renderer. Wrapping a GL
+     * object enables examining and modifying the behavior of the
+     * GL calls made by the renderer.
+     * <p>
+     * Wrapping is typically used for debugging purposes.
+     * <p>
+     * The default value is null.
+     * @param glWrapper the new GLWrapper
+     */
+    public void setGLWrapper(GLWrapper glWrapper) {
+        mGLWrapper = glWrapper;
+    }
+
+    /**
+     * Set the debug flags to a new value. The value is
+     * constructed by OR-together zero or more
+     * of the DEBUG_CHECK_* constants. The debug flags take effect
+     * whenever a surface is created. The default value is zero.
+     * @param debugFlags the new debug flags
+     * @see #DEBUG_CHECK_GL_ERROR
+     * @see #DEBUG_LOG_GL_CALLS
+     */
+    public void setDebugFlags(int debugFlags) {
+        mDebugFlags = debugFlags;
+    }
+
+    /**
+     * Get the current value of the debug flags.
+     * @return the current value of the debug flags.
+     */
+    public int getDebugFlags() {
+        return mDebugFlags;
+    }
+
+    /**
+     * Control whether the EGL context is preserved when the GLSurfaceView is paused and
+     * resumed.
+     * <p>
+     * If set to true, then the EGL context may be preserved when the GLSurfaceView is paused.
+     * Whether the EGL context is actually preserved or not depends upon whether the
+     * Android device that the program is running on can support an arbitrary number of EGL
+     * contexts or not. Devices that can only support a limited number of EGL contexts must
+     * release the  EGL context in order to allow multiple applications to share the GPU.
+     * <p>
+     * If set to false, the EGL context will be released when the GLSurfaceView is paused,
+     * and recreated when the GLSurfaceView is resumed.
+     * <p>
+     *
+     * The default is false.
+     *
+     * @param preserveOnPause preserve the EGL context when paused
+     */
+    public void setPreserveEGLContextOnPause(boolean preserveOnPause) {
+        mPreserveEGLContextOnPause = preserveOnPause;
+    }
+
+    /**
+     * @return true if the EGL context will be preserved when paused
+     */
+    public boolean getPreserveEGLContextOnPause() {
+        return mPreserveEGLContextOnPause;
+    }
+
+    /**
+     * Set the renderer associated with this view. Also starts the thread that
+     * will call the renderer, which in turn causes the rendering to start.
+     * <p>This method should be called once and only once in the life-cycle of
+     * a GLSurfaceView.
+     * <p>The following GLSurfaceView methods can only be called <em>before</em>
+     * setRenderer is called:
+     * <ul>
+     * <li>{@link #setEGLConfigChooser(boolean)}
+     * <li>{@link #setEGLConfigChooser(EGLConfigChooser)}
+     * <li>{@link #setEGLConfigChooser(int, int, int, int, int, int)}
+     * </ul>
+     * <p>
+     * The following GLSurfaceView methods can only be called <em>after</em>
+     * setRenderer is called:
+     * <ul>
+     * <li>{@link #getRenderMode()}
+     * <li>{@link #onPause()}
+     * <li>{@link #onResume()}
+     * <li>{@link #queueEvent(Runnable)}
+     * <li>{@link #requestRender()}
+     * <li>{@link #setRenderMode(int)}
+     * </ul>
+     *
+     * @param renderer the renderer to use to perform OpenGL drawing.
+     */
+    public void setRenderer(Renderer renderer) {
+        checkRenderThreadState();
+        if (mEGLConfigChooser == null) {
+            mEGLConfigChooser = new SimpleEGLConfigChooser(true);
+        }
+        if (mEGLContextFactory == null) {
+            mEGLContextFactory = new DefaultContextFactory();
+        }
+        if (mEGLWindowSurfaceFactory == null) {
+            mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
+        }
+        mRenderer = renderer;
+        mGLThread = new GLThread(mThisWeakRef);
+        mGLThread.start();
+    }
+
+    /**
+     * Install a custom EGLContextFactory.
+     * <p>If this method is
+     * called, it must be called before {@link #setRenderer(Renderer)}
+     * is called.
+     * <p>
+     * If this method is not called, then by default
+     * a context will be created with no shared context and
+     * with a null attribute list.
+     */
+    public void setEGLContextFactory(EGLContextFactory factory) {
+        checkRenderThreadState();
+        mEGLContextFactory = factory;
+    }
+
+    /**
+     * Install a custom EGLWindowSurfaceFactory.
+     * <p>If this method is
+     * called, it must be called before {@link #setRenderer(Renderer)}
+     * is called.
+     * <p>
+     * If this method is not called, then by default
+     * a window surface will be created with a null attribute list.
+     */
+    public void setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory factory) {
+        checkRenderThreadState();
+        mEGLWindowSurfaceFactory = factory;
+    }
+
+    /**
+     * Install a custom EGLConfigChooser.
+     * <p>If this method is
+     * called, it must be called before {@link #setRenderer(Renderer)}
+     * is called.
+     * <p>
+     * If no setEGLConfigChooser method is called, then by default the
+     * view will choose an EGLConfig that is compatible with the current
+     * android.view.Surface, with a depth buffer depth of
+     * at least 16 bits.
+     * @param configChooser
+     */
+    public void setEGLConfigChooser(EGLConfigChooser configChooser) {
+        checkRenderThreadState();
+        mEGLConfigChooser = configChooser;
+    }
+
+    /**
+     * Install a config chooser which will choose a config
+     * as close to 16-bit RGB as possible, with or without an optional depth
+     * buffer as close to 16-bits as possible.
+     * <p>If this method is
+     * called, it must be called before {@link #setRenderer(Renderer)}
+     * is called.
+     * <p>
+     * If no setEGLConfigChooser method is called, then by default the
+     * view will choose an RGB_888 surface with a depth buffer depth of
+     * at least 16 bits.
+     *
+     * @param needDepth
+     */
+    public void setEGLConfigChooser(boolean needDepth) {
+        setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth));
+    }
+
+    /**
+     * Install a config chooser which will choose a config
+     * with at least the specified depthSize and stencilSize,
+     * and exactly the specified redSize, greenSize, blueSize and alphaSize.
+     * <p>If this method is
+     * called, it must be called before {@link #setRenderer(Renderer)}
+     * is called.
+     * <p>
+     * If no setEGLConfigChooser method is called, then by default the
+     * view will choose an RGB_888 surface with a depth buffer depth of
+     * at least 16 bits.
+     *
+     */
+    public void setEGLConfigChooser(int redSize, int greenSize, int blueSize,
+            int alphaSize, int depthSize, int stencilSize) {
+        setEGLConfigChooser(new ComponentSizeChooser(redSize, greenSize,
+                blueSize, alphaSize, depthSize, stencilSize));
+    }
+
+    /**
+     * Inform the default EGLContextFactory and default EGLConfigChooser
+     * which EGLContext client version to pick.
+     * <p>Use this method to create an OpenGL ES 2.0-compatible context.
+     * Example:
+     * <pre class="prettyprint">
+     *     public MyView(Context context) {
+     *         super(context);
+     *         setEGLContextClientVersion(2); // Pick an OpenGL ES 2.0 context.
+     *         setRenderer(new MyRenderer());
+     *     }
+     * </pre>
+     * <p>Note: Activities which require OpenGL ES 2.0 should indicate this by
+     * setting @lt;uses-feature android:glEsVersion="0x00020000" /> in the activity's
+     * AndroidManifest.xml file.
+     * <p>If this method is called, it must be called before {@link #setRenderer(Renderer)}
+     * is called.
+     * <p>This method only affects the behavior of the default EGLContexFactory and the
+     * default EGLConfigChooser. If
+     * {@link #setEGLContextFactory(EGLContextFactory)} has been called, then the supplied
+     * EGLContextFactory is responsible for creating an OpenGL ES 2.0-compatible context.
+     * If
+     * {@link #setEGLConfigChooser(EGLConfigChooser)} has been called, then the supplied
+     * EGLConfigChooser is responsible for choosing an OpenGL ES 2.0-compatible config.
+     * @param version The EGLContext client version to choose. Use 2 for OpenGL ES 2.0
+     */
+    public void setEGLContextClientVersion(int version) {
+        checkRenderThreadState();
+        mEGLContextClientVersion = version;
+    }
+
+    /**
+     * Set the rendering mode. When renderMode is
+     * RENDERMODE_CONTINUOUSLY, the renderer is called
+     * repeatedly to re-render the scene. When renderMode
+     * is RENDERMODE_WHEN_DIRTY, the renderer only rendered when the surface
+     * is created, or when {@link #requestRender} is called. Defaults to RENDERMODE_CONTINUOUSLY.
+     * <p>
+     * Using RENDERMODE_WHEN_DIRTY can improve battery life and overall system performance
+     * by allowing the GPU and CPU to idle when the view does not need to be updated.
+     * <p>
+     * This method can only be called after {@link #setRenderer(Renderer)}
+     *
+     * @param renderMode one of the RENDERMODE_X constants
+     * @see #RENDERMODE_CONTINUOUSLY
+     * @see #RENDERMODE_WHEN_DIRTY
+     */
+    public void setRenderMode(int renderMode) {
+        mGLThread.setRenderMode(renderMode);
+    }
+
+    /**
+     * Get the current rendering mode. May be called
+     * from any thread. Must not be called before a renderer has been set.
+     * @return the current rendering mode.
+     * @see #RENDERMODE_CONTINUOUSLY
+     * @see #RENDERMODE_WHEN_DIRTY
+     */
+    public int getRenderMode() {
+        return mGLThread.getRenderMode();
+    }
+
+    /**
+     * Request that the renderer render a frame.
+     * This method is typically used when the render mode has been set to
+     * {@link #RENDERMODE_WHEN_DIRTY}, so that frames are only rendered on demand.
+     * May be called
+     * from any thread. Must not be called before a renderer has been set.
+     */
+    public void requestRender() {
+        mGLThread.requestRender();
+    }
+
+    /**
+     * This method is part of the SurfaceHolder.Callback interface, and is
+     * not normally called or subclassed by clients of GLSurfaceView.
+     */
+    public void surfaceCreated(SurfaceHolder holder) {
+        mGLThread.surfaceCreated();
+    }
+
+    /**
+     * This method is part of the SurfaceHolder.Callback interface, and is
+     * not normally called or subclassed by clients of GLSurfaceView.
+     */
+    public void surfaceDestroyed(SurfaceHolder holder) {
+        // Surface will be destroyed when we return
+        mGLThread.surfaceDestroyed();
+    }
+
+    /**
+     * This method is part of the SurfaceHolder.Callback interface, and is
+     * not normally called or subclassed by clients of GLSurfaceView.
+     */
+    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+        mGLThread.onWindowResize(w, h);
+    }
+
+    /**
+     * Inform the view that the activity is paused. The owner of this view must
+     * call this method when the activity is paused. Calling this method will
+     * pause the rendering thread.
+     * Must not be called before a renderer has been set.
+     */
+    public void onPause() {
+        mGLThread.onPause();
+    }
+
+    /**
+     * Inform the view that the activity is resumed. The owner of this view must
+     * call this method when the activity is resumed. Calling this method will
+     * recreate the OpenGL display and resume the rendering
+     * thread.
+     * Must not be called before a renderer has been set.
+     */
+    public void onResume() {
+        mGLThread.onResume();
+    }
+
+    /**
+     * Queue a runnable to be run on the GL rendering thread. This can be used
+     * to communicate with the Renderer on the rendering thread.
+     * Must not be called before a renderer has been set.
+     * @param r the runnable to be run on the GL rendering thread.
+     */
+    public void queueEvent(Runnable r) {
+        mGLThread.queueEvent(r);
+    }
+
+    /**
+     * This method is used as part of the View class and is not normally
+     * called or subclassed by clients of GLSurfaceView.
+     */
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        if (LOG_ATTACH_DETACH) {
+            Log.d(TAG, "onAttachedToWindow reattach =" + mDetached);
+        }
+        if (mDetached && (mRenderer != null)) {
+            int renderMode = RENDERMODE_CONTINUOUSLY;
+            if (mGLThread != null) {
+                renderMode = mGLThread.getRenderMode();
+            }
+            mGLThread = new GLThread(mThisWeakRef);
+            if (renderMode != RENDERMODE_CONTINUOUSLY) {
+                mGLThread.setRenderMode(renderMode);
+            }
+            mGLThread.start();
+        }
+        mDetached = false;
+    }
+
+    /**
+     * This method is used as part of the View class and is not normally
+     * called or subclassed by clients of GLSurfaceView.
+     * Must not be called before a renderer has been set.
+     */
+    @Override
+    protected void onDetachedFromWindow() {
+        if (LOG_ATTACH_DETACH) {
+            Log.d(TAG, "onDetachedFromWindow");
+        }
+        if (mGLThread != null) {
+            mGLThread.requestExitAndWait();
+        }
+        mDetached = true;
+        super.onDetachedFromWindow();
+    }
+
+    // ----------------------------------------------------------------------
+
+    /**
+     * An interface used to wrap a GL interface.
+     * <p>Typically
+     * used for implementing debugging and tracing on top of the default
+     * GL interface. You would typically use this by creating your own class
+     * that implemented all the GL methods by delegating to another GL instance.
+     * Then you could add your own behavior before or after calling the
+     * delegate. All the GLWrapper would do was instantiate and return the
+     * wrapper GL instance:
+     * <pre class="prettyprint">
+     * class MyGLWrapper implements GLWrapper {
+     *     GL wrap(GL gl) {
+     *         return new MyGLImplementation(gl);
+     *     }
+     *     static class MyGLImplementation implements GL,GL10,GL11,... {
+     *         ...
+     *     }
+     * }
+     * </pre>
+     * @see #setGLWrapper(GLWrapper)
+     */
+    public interface GLWrapper {
+        /**
+         * Wraps a gl interface in another gl interface.
+         * @param gl a GL interface that is to be wrapped.
+         * @return either the input argument or another GL object that wraps the input argument.
+         */
+        GL wrap(GL gl);
+    }
+
+    /**
+     * A generic renderer interface.
+     * <p>
+     * The renderer is responsible for making OpenGL calls to render a frame.
+     * <p>
+     * GLSurfaceView clients typically create their own classes that implement
+     * this interface, and then call {@link GLSurfaceView#setRenderer} to
+     * register the renderer with the GLSurfaceView.
+     * <p>
+     *
+     * <div class="special reference">
+     * <h3>Developer Guides</h3>
+     * <p>For more information about how to use OpenGL, read the
+     * <a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL</a> developer guide.</p>
+     * </div>
+     *
+     * <h3>Threading</h3>
+     * The renderer will be called on a separate thread, so that rendering
+     * performance is decoupled from the UI thread. Clients typically need to
+     * communicate with the renderer from the UI thread, because that's where
+     * input events are received. Clients can communicate using any of the
+     * standard Java techniques for cross-thread communication, or they can
+     * use the {@link GLSurfaceView#queueEvent(Runnable)} convenience method.
+     * <p>
+     * <h3>EGL Context Lost</h3>
+     * There are situations where the EGL rendering context will be lost. This
+     * typically happens when device wakes up after going to sleep. When
+     * the EGL context is lost, all OpenGL resources (such as textures) that are
+     * associated with that context will be automatically deleted. In order to
+     * keep rendering correctly, a renderer must recreate any lost resources
+     * that it still needs. The {@link #onSurfaceCreated(GL10, EGLConfig)} method
+     * is a convenient place to do this.
+     *
+     *
+     * @see #setRenderer(Renderer)
+     */
+    public interface Renderer {
+        /**
+         * Called when the surface is created or recreated.
+         * <p>
+         * Called when the rendering thread
+         * starts and whenever the EGL context is lost. The EGL context will typically
+         * be lost when the Android device awakes after going to sleep.
+         * <p>
+         * Since this method is called at the beginning of rendering, as well as
+         * every time the EGL context is lost, this method is a convenient place to put
+         * code to create resources that need to be created when the rendering
+         * starts, and that need to be recreated when the EGL context is lost.
+         * Textures are an example of a resource that you might want to create
+         * here.
+         * <p>
+         * Note that when the EGL context is lost, all OpenGL resources associated
+         * with that context will be automatically deleted. You do not need to call
+         * the corresponding "glDelete" methods such as glDeleteTextures to
+         * manually delete these lost resources.
+         * <p>
+         * @param gl the GL interface. Use <code>instanceof</code> to
+         * test if the interface supports GL11 or higher interfaces.
+         * @param config the EGLConfig of the created surface. Can be used
+         * to create matching pbuffers.
+         */
+        void onSurfaceCreated(GL10 gl, EGLConfig config);
+
+        /**
+         * Called when the surface changed size.
+         * <p>
+         * Called after the surface is created and whenever
+         * the OpenGL ES surface size changes.
+         * <p>
+         * Typically you will set your viewport here. If your camera
+         * is fixed then you could also set your projection matrix here:
+         * <pre class="prettyprint">
+         * void onSurfaceChanged(GL10 gl, int width, int height) {
+         *     gl.glViewport(0, 0, width, height);
+         *     // for a fixed camera, set the projection too
+         *     float ratio = (float) width / height;
+         *     gl.glMatrixMode(GL10.GL_PROJECTION);
+         *     gl.glLoadIdentity();
+         *     gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
+         * }
+         * </pre>
+         * @param gl the GL interface. Use <code>instanceof</code> to
+         * test if the interface supports GL11 or higher interfaces.
+         * @param width
+         * @param height
+         */
+        void onSurfaceChanged(GL10 gl, int width, int height);
+
+        /**
+         * Called to draw the current frame.
+         * <p>
+         * This method is responsible for drawing the current frame.
+         * <p>
+         * The implementation of this method typically looks like this:
+         * <pre class="prettyprint">
+         * void onDrawFrame(GL10 gl) {
+         *     gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
+         *     //... other gl calls to render the scene ...
+         * }
+         * </pre>
+         * @param gl the GL interface. Use <code>instanceof</code> to
+         * test if the interface supports GL11 or higher interfaces.
+         */
+        void onDrawFrame(GL10 gl);
+
+        /**
+         * called after eglSwapBuffers() is called.
+         * can be used for custom synchronization / measurement.
+         */
+        void onEglSwapBuffers();
+    }
+
+    /**
+     * An interface for customizing the eglCreateContext and eglDestroyContext calls.
+     * <p>
+     * This interface must be implemented by clients wishing to call
+     * {@link GLSurfaceView#setEGLContextFactory(EGLContextFactory)}
+     */
+    public interface EGLContextFactory {
+        EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig);
+        void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context);
+    }
+
+    private class DefaultContextFactory implements EGLContextFactory {
+        private int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+
+        public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) {
+            int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, mEGLContextClientVersion,
+                    EGL10.EGL_NONE };
+
+            return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT,
+                    mEGLContextClientVersion != 0 ? attrib_list : null);
+        }
+
+        public void destroyContext(EGL10 egl, EGLDisplay display,
+                EGLContext context) {
+            if (!egl.eglDestroyContext(display, context)) {
+                Log.e("DefaultContextFactory", "display:" + display + " context: " + context);
+                if (LOG_THREADS) {
+                    Log.i("DefaultContextFactory", "tid=" + Thread.currentThread().getId());
+                }
+                EglHelper.throwEglException("eglDestroyContex", egl.eglGetError());
+            }
+        }
+    }
+
+    /**
+     * An interface for customizing the eglCreateWindowSurface and eglDestroySurface calls.
+     * <p>
+     * This interface must be implemented by clients wishing to call
+     * {@link GLSurfaceView#setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory)}
+     */
+    public interface EGLWindowSurfaceFactory {
+        /**
+         *  @return null if the surface cannot be constructed.
+         */
+        EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config,
+                Object nativeWindow);
+        void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface);
+    }
+
+    private static class DefaultWindowSurfaceFactory implements EGLWindowSurfaceFactory {
+
+        public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display,
+                EGLConfig config, Object nativeWindow) {
+            EGLSurface result = null;
+            try {
+                result = egl.eglCreateWindowSurface(display, config, nativeWindow, null);
+            } catch (IllegalArgumentException e) {
+                // This exception indicates that the surface flinger surface
+                // is not valid. This can happen if the surface flinger surface has
+                // been torn down, but the application has not yet been
+                // notified via SurfaceHolder.Callback.surfaceDestroyed.
+                // In theory the application should be notified first,
+                // but in practice sometimes it is not. See b/4588890
+                Log.e(TAG, "eglCreateWindowSurface", e);
+            }
+            return result;
+        }
+
+        public void destroySurface(EGL10 egl, EGLDisplay display,
+                EGLSurface surface) {
+            egl.eglDestroySurface(display, surface);
+        }
+    }
+
+    /**
+     * An interface for choosing an EGLConfig configuration from a list of
+     * potential configurations.
+     * <p>
+     * This interface must be implemented by clients wishing to call
+     * {@link GLSurfaceView#setEGLConfigChooser(EGLConfigChooser)}
+     */
+    public interface EGLConfigChooser {
+        /**
+         * Choose a configuration from the list. Implementors typically
+         * implement this method by calling
+         * {@link EGL10#eglChooseConfig} and iterating through the results. Please consult the
+         * EGL specification available from The Khronos Group to learn how to call eglChooseConfig.
+         * @param egl the EGL10 for the current display.
+         * @param display the current display.
+         * @return the chosen configuration.
+         */
+        EGLConfig chooseConfig(EGL10 egl, EGLDisplay display);
+    }
+
+    private abstract class BaseConfigChooser
+            implements EGLConfigChooser {
+        public BaseConfigChooser(int[] configSpec) {
+            mConfigSpec = filterConfigSpec(configSpec);
+        }
+
+        public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
+            int[] num_config = new int[1];
+            if (!egl.eglChooseConfig(display, mConfigSpec, null, 0,
+                    num_config)) {
+                throw new IllegalArgumentException("eglChooseConfig failed");
+            }
+
+            int numConfigs = num_config[0];
+
+            if (numConfigs <= 0) {
+                throw new IllegalArgumentException(
+                        "No configs match configSpec");
+            }
+
+            EGLConfig[] configs = new EGLConfig[numConfigs];
+            if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs,
+                    num_config)) {
+                throw new IllegalArgumentException("eglChooseConfig#2 failed");
+            }
+            EGLConfig config = chooseConfig(egl, display, configs);
+            if (config == null) {
+                throw new IllegalArgumentException("No config chosen");
+            }
+            return config;
+        }
+
+        abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
+                EGLConfig[] configs);
+
+        protected int[] mConfigSpec;
+
+        private int[] filterConfigSpec(int[] configSpec) {
+            if (mEGLContextClientVersion != 2) {
+                return configSpec;
+            }
+            /* We know none of the subclasses define EGL_RENDERABLE_TYPE.
+             * And we know the configSpec is well formed.
+             */
+            int len = configSpec.length;
+            int[] newConfigSpec = new int[len + 2];
+            System.arraycopy(configSpec, 0, newConfigSpec, 0, len-1);
+            newConfigSpec[len-1] = EGL10.EGL_RENDERABLE_TYPE;
+            newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */
+            newConfigSpec[len+1] = EGL10.EGL_NONE;
+            return newConfigSpec;
+        }
+    }
+
+    /**
+     * Choose a configuration with exactly the specified r,g,b,a sizes,
+     * and at least the specified depth and stencil sizes.
+     */
+    private class ComponentSizeChooser extends BaseConfigChooser {
+        public ComponentSizeChooser(int redSize, int greenSize, int blueSize,
+                int alphaSize, int depthSize, int stencilSize) {
+            super(new int[] {
+                    EGL10.EGL_RED_SIZE, redSize,
+                    EGL10.EGL_GREEN_SIZE, greenSize,
+                    EGL10.EGL_BLUE_SIZE, blueSize,
+                    EGL10.EGL_ALPHA_SIZE, alphaSize,
+                    EGL10.EGL_DEPTH_SIZE, depthSize,
+                    EGL10.EGL_STENCIL_SIZE, stencilSize,
+                    EGL10.EGL_NONE});
+            mValue = new int[1];
+            mRedSize = redSize;
+            mGreenSize = greenSize;
+            mBlueSize = blueSize;
+            mAlphaSize = alphaSize;
+            mDepthSize = depthSize;
+            mStencilSize = stencilSize;
+       }
+
+        @Override
+        public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
+                EGLConfig[] configs) {
+            for (EGLConfig config : configs) {
+                int d = findConfigAttrib(egl, display, config,
+                        EGL10.EGL_DEPTH_SIZE, 0);
+                int s = findConfigAttrib(egl, display, config,
+                        EGL10.EGL_STENCIL_SIZE, 0);
+                if ((d >= mDepthSize) && (s >= mStencilSize)) {
+                    int r = findConfigAttrib(egl, display, config,
+                            EGL10.EGL_RED_SIZE, 0);
+                    int g = findConfigAttrib(egl, display, config,
+                             EGL10.EGL_GREEN_SIZE, 0);
+                    int b = findConfigAttrib(egl, display, config,
+                              EGL10.EGL_BLUE_SIZE, 0);
+                    int a = findConfigAttrib(egl, display, config,
+                            EGL10.EGL_ALPHA_SIZE, 0);
+                    if ((r == mRedSize) && (g == mGreenSize)
+                            && (b == mBlueSize) && (a == mAlphaSize)) {
+                        return config;
+                    }
+                }
+            }
+            return null;
+        }
+
+        private int findConfigAttrib(EGL10 egl, EGLDisplay display,
+                EGLConfig config, int attribute, int defaultValue) {
+
+            if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
+                return mValue[0];
+            }
+            return defaultValue;
+        }
+
+        private int[] mValue;
+        // Subclasses can adjust these values:
+        protected int mRedSize;
+        protected int mGreenSize;
+        protected int mBlueSize;
+        protected int mAlphaSize;
+        protected int mDepthSize;
+        protected int mStencilSize;
+        }
+
+    /**
+     * This class will choose a RGB_888 surface with
+     * or without a depth buffer.
+     *
+     */
+    private class SimpleEGLConfigChooser extends ComponentSizeChooser {
+        public SimpleEGLConfigChooser(boolean withDepthBuffer) {
+            super(8, 8, 8, 0, withDepthBuffer ? 16 : 0, 0);
+        }
+    }
+
+    /**
+     * An EGL helper class.
+     */
+
+    private static class EglHelper {
+        public EglHelper(WeakReference<GLSurfaceViewCustom> glSurfaceViewWeakRef) {
+            mGLSurfaceViewWeakRef = glSurfaceViewWeakRef;
+        }
+
+        /**
+         * Initialize EGL for a given configuration spec.
+         * @param configSpec
+         */
+        public void start() {
+            if (LOG_EGL) {
+                Log.w("EglHelper", "start() tid=" + Thread.currentThread().getId());
+            }
+            /*
+             * Get an EGL instance
+             */
+            mEgl = (EGL10) EGLContext.getEGL();
+
+            /*
+             * Get to the default display.
+             */
+            mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+
+            if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
+                throw new RuntimeException("eglGetDisplay failed");
+            }
+
+            /*
+             * We can now initialize EGL for that display
+             */
+            int[] version = new int[2];
+            if(!mEgl.eglInitialize(mEglDisplay, version)) {
+                throw new RuntimeException("eglInitialize failed");
+            }
+            GLSurfaceViewCustom view = mGLSurfaceViewWeakRef.get();
+            if (view == null) {
+                mEglConfig = null;
+                mEglContext = null;
+            } else {
+                mEglConfig = view.mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);
+
+                /*
+                * Create an EGL context. We want to do this as rarely as we can, because an
+                * EGL context is a somewhat heavy object.
+                */
+                mEglContext = view.mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);
+            }
+            if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {
+                mEglContext = null;
+                throwEglException("createContext");
+            }
+            if (LOG_EGL) {
+                Log.w("EglHelper", "createContext " + mEglContext + " tid=" + Thread.currentThread().getId());
+            }
+
+            mEglSurface = null;
+        }
+
+        /**
+         * Create an egl surface for the current SurfaceHolder surface. If a surface
+         * already exists, destroy it before creating the new surface.
+         *
+         * @return true if the surface was created successfully.
+         */
+        public boolean createSurface() {
+            if (LOG_EGL) {
+                Log.w("EglHelper", "createSurface()  tid=" + Thread.currentThread().getId());
+            }
+            /*
+             * Check preconditions.
+             */
+            if (mEgl == null) {
+                throw new RuntimeException("egl not initialized");
+            }
+            if (mEglDisplay == null) {
+                throw new RuntimeException("eglDisplay not initialized");
+            }
+            if (mEglConfig == null) {
+                throw new RuntimeException("mEglConfig not initialized");
+            }
+
+            /*
+             *  The window size has changed, so we need to create a new
+             *  surface.
+             */
+            destroySurfaceImp();
+
+            /*
+             * Create an EGL surface we can render into.
+             */
+            GLSurfaceViewCustom view = mGLSurfaceViewWeakRef.get();
+            if (view != null) {
+                mEglSurface = view.mEGLWindowSurfaceFactory.createWindowSurface(mEgl,
+                        mEglDisplay, mEglConfig, view.getHolder());
+            } else {
+                mEglSurface = null;
+            }
+
+            if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
+                int error = mEgl.eglGetError();
+                if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
+                    Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
+                }
+                return false;
+            }
+
+            /*
+             * Before we can issue GL commands, we need to make sure
+             * the context is current and bound to a surface.
+             */
+            if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+                /*
+                 * Could not make the context current, probably because the underlying
+                 * SurfaceView surface has been destroyed.
+                 */
+                logEglErrorAsWarning("EGLHelper", "eglMakeCurrent", mEgl.eglGetError());
+                return false;
+            }
+
+            return true;
+        }
+
+        /**
+         * Create a GL object for the current EGL context.
+         * @return
+         */
+        GL createGL() {
+
+            GL gl = mEglContext.getGL();
+            GLSurfaceViewCustom view = mGLSurfaceViewWeakRef.get();
+            if (view != null) {
+                if (view.mGLWrapper != null) {
+                    gl = view.mGLWrapper.wrap(gl);
+                }
+
+/*                if ((view.mDebugFlags & (DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS)) != 0) {
+                    int configFlags = 0;
+                    Writer log = null;
+                    if ((view.mDebugFlags & DEBUG_CHECK_GL_ERROR) != 0) {
+                        configFlags |= GLDebugHelper.CONFIG_CHECK_GL_ERROR;
+                    }
+                    if ((view.mDebugFlags & DEBUG_LOG_GL_CALLS) != 0) {
+                        log = new LogWriter();
+                    }
+                    gl = GLDebugHelper.wrap(gl, configFlags, log);
+                }*/
+            }
+            return gl;
+        }
+
+        /**
+         * Display the current render surface.
+         * @return the EGL error code from eglSwapBuffers.
+         */
+        public int swap() {
+            if (! mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
+                return mEgl.eglGetError();
+            }
+            return EGL10.EGL_SUCCESS;
+        }
+
+        public void destroySurface() {
+            if (LOG_EGL) {
+                Log.w("EglHelper", "destroySurface()  tid=" + Thread.currentThread().getId());
+            }
+            destroySurfaceImp();
+        }
+
+        private void destroySurfaceImp() {
+            if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
+                mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
+                        EGL10.EGL_NO_SURFACE,
+                        EGL10.EGL_NO_CONTEXT);
+                GLSurfaceViewCustom view = mGLSurfaceViewWeakRef.get();
+                if (view != null) {
+                    view.mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface);
+                }
+                mEglSurface = null;
+            }
+        }
+
+        public void finish() {
+            if (LOG_EGL) {
+                Log.w("EglHelper", "finish() tid=" + Thread.currentThread().getId());
+            }
+            if (mEglContext != null) {
+                GLSurfaceViewCustom view = mGLSurfaceViewWeakRef.get();
+                if (view != null) {
+                    view.mEGLContextFactory.destroyContext(mEgl, mEglDisplay, mEglContext);
+                }
+                mEglContext = null;
+            }
+            if (mEglDisplay != null) {
+                mEgl.eglTerminate(mEglDisplay);
+                mEglDisplay = null;
+            }
+        }
+
+        private void throwEglException(String function) {
+            throwEglException(function, mEgl.eglGetError());
+        }
+
+        public static void throwEglException(String function, int error) {
+            String message = formatEglError(function, error);
+            if (LOG_THREADS) {
+                Log.e("EglHelper", "throwEglException tid=" + Thread.currentThread().getId() + " "
+                        + message);
+            }
+            throw new RuntimeException(message);
+        }
+
+        public static void logEglErrorAsWarning(String tag, String function, int error) {
+            Log.w(tag, formatEglError(function, error));
+        }
+
+        public static String formatEglError(String function, int error) {
+            return function + " failed: " + error;
+        }
+
+        private WeakReference<GLSurfaceViewCustom> mGLSurfaceViewWeakRef;
+        EGL10 mEgl;
+        EGLDisplay mEglDisplay;
+        EGLSurface mEglSurface;
+        EGLConfig mEglConfig;
+        EGLContext mEglContext;
+
+    }
+
+    /**
+     * A generic GL Thread. Takes care of initializing EGL and GL. Delegates
+     * to a Renderer instance to do the actual drawing. Can be configured to
+     * render continuously or on request.
+     *
+     * All potentially blocking synchronization is done through the
+     * sGLThreadManager object. This avoids multiple-lock ordering issues.
+     *
+     */
+    static class GLThread extends Thread {
+        GLThread(WeakReference<GLSurfaceViewCustom> glSurfaceViewWeakRef) {
+            super();
+            mWidth = 0;
+            mHeight = 0;
+            mRequestRender = true;
+            mRenderMode = RENDERMODE_CONTINUOUSLY;
+            mGLSurfaceViewWeakRef = glSurfaceViewWeakRef;
+        }
+
+        @Override
+        public void run() {
+            setName("GLThread " + getId());
+            if (LOG_THREADS) {
+                Log.i("GLThread", "starting tid=" + getId());
+            }
+
+            try {
+                guardedRun();
+            } catch (InterruptedException e) {
+                // fall thru and exit normally
+            } finally {
+                sGLThreadManager.threadExiting(this);
+            }
+        }
+
+        /*
+         * This private method should only be called inside a
+         * synchronized(sGLThreadManager) block.
+         */
+        private void stopEglSurfaceLocked() {
+            if (mHaveEglSurface) {
+                mHaveEglSurface = false;
+                mEglHelper.destroySurface();
+            }
+        }
+
+        /*
+         * This private method should only be called inside a
+         * synchronized(sGLThreadManager) block.
+         */
+        private void stopEglContextLocked() {
+            if (mHaveEglContext) {
+                mEglHelper.finish();
+                mHaveEglContext = false;
+                sGLThreadManager.releaseEglContextLocked(this);
+            }
+        }
+        private void guardedRun() throws InterruptedException {
+            mEglHelper = new EglHelper(mGLSurfaceViewWeakRef);
+            mHaveEglContext = false;
+            mHaveEglSurface = false;
+            try {
+                GL10 gl = null;
+                boolean createEglContext = false;
+                boolean createEglSurface = false;
+                boolean createGlInterface = false;
+                boolean lostEglContext = false;
+                boolean sizeChanged = false;
+                boolean wantRenderNotification = false;
+                boolean doRenderNotification = false;
+                boolean askedToReleaseEglContext = false;
+                int w = 0;
+                int h = 0;
+                Runnable event = null;
+
+                while (true) {
+                    synchronized (sGLThreadManager) {
+                        while (true) {
+                            if (mShouldExit) {
+                                return;
+                            }
+
+                            if (! mEventQueue.isEmpty()) {
+                                event = mEventQueue.remove(0);
+                                break;
+                            }
+
+                            // Update the pause state.
+                            boolean pausing = false;
+                            if (mPaused != mRequestPaused) {
+                                pausing = mRequestPaused;
+                                mPaused = mRequestPaused;
+                                sGLThreadManager.notifyAll();
+                                if (LOG_PAUSE_RESUME) {
+                                    Log.i("GLThread", "mPaused is now " + mPaused + " tid=" + getId());
+                                }
+                            }
+
+                            // Do we need to give up the EGL context?
+                            if (mShouldReleaseEglContext) {
+                                if (LOG_SURFACE) {
+                                    Log.i("GLThread", "releasing EGL context because asked to tid=" + getId());
+                                }
+                                stopEglSurfaceLocked();
+                                stopEglContextLocked();
+                                mShouldReleaseEglContext = false;
+                                askedToReleaseEglContext = true;
+                            }
+
+                            // Have we lost the EGL context?
+                            if (lostEglContext) {
+                                stopEglSurfaceLocked();
+                                stopEglContextLocked();
+                                lostEglContext = false;
+                            }
+
+                            // When pausing, release the EGL surface:
+                            if (pausing && mHaveEglSurface) {
+                                if (LOG_SURFACE) {
+                                    Log.i("GLThread", "releasing EGL surface because paused tid=" + getId());
+                                }
+                                stopEglSurfaceLocked();
+                            }
+
+                            // When pausing, optionally release the EGL Context:
+                            if (pausing && mHaveEglContext) {
+                                GLSurfaceViewCustom view = mGLSurfaceViewWeakRef.get();
+                                boolean preserveEglContextOnPause = view == null ?
+                                        false : view.mPreserveEGLContextOnPause;
+                                if (!preserveEglContextOnPause || sGLThreadManager.shouldReleaseEGLContextWhenPausing()) {
+                                    stopEglContextLocked();
+                                    if (LOG_SURFACE) {
+                                        Log.i("GLThread", "releasing EGL context because paused tid=" + getId());
+                                    }
+                                }
+                            }
+
+                            // When pausing, optionally terminate EGL:
+                            if (pausing) {
+                                if (sGLThreadManager.shouldTerminateEGLWhenPausing()) {
+                                    mEglHelper.finish();
+                                    if (LOG_SURFACE) {
+                                        Log.i("GLThread", "terminating EGL because paused tid=" + getId());
+                                    }
+                                }
+                            }
+
+                            // Have we lost the SurfaceView surface?
+                            if ((! mHasSurface) && (! mWaitingForSurface)) {
+                                if (LOG_SURFACE) {
+                                    Log.i("GLThread", "noticed surfaceView surface lost tid=" + getId());
+                                }
+                                if (mHaveEglSurface) {
+                                    stopEglSurfaceLocked();
+                                }
+                                mWaitingForSurface = true;
+                                mSurfaceIsBad = false;
+                                sGLThreadManager.notifyAll();
+                            }
+
+                            // Have we acquired the surface view surface?
+                            if (mHasSurface && mWaitingForSurface) {
+                                if (LOG_SURFACE) {
+                                    Log.i("GLThread", "noticed surfaceView surface acquired tid=" + getId());
+                                }
+                                mWaitingForSurface = false;
+                                sGLThreadManager.notifyAll();
+                            }
+
+                            if (doRenderNotification) {
+                                if (LOG_SURFACE) {
+                                    Log.i("GLThread", "sending render notification tid=" + getId());
+                                }
+                                wantRenderNotification = false;
+                                doRenderNotification = false;
+                                mRenderComplete = true;
+                                sGLThreadManager.notifyAll();
+                            }
+
+                            // Ready to draw?
+                            if (readyToDraw()) {
+
+                                // If we don't have an EGL context, try to acquire one.
+                                if (! mHaveEglContext) {
+                                    if (askedToReleaseEglContext) {
+                                        askedToReleaseEglContext = false;
+                                    } else if (sGLThreadManager.tryAcquireEglContextLocked(this)) {
+                                        try {
+                                            mEglHelper.start();
+                                        } catch (RuntimeException t) {
+                                            sGLThreadManager.releaseEglContextLocked(this);
+                                            throw t;
+                                        }
+                                        mHaveEglContext = true;
+                                        createEglContext = true;
+
+                                        sGLThreadManager.notifyAll();
+                                    }
+                                }
+
+                                if (mHaveEglContext && !mHaveEglSurface) {
+                                    mHaveEglSurface = true;
+                                    createEglSurface = true;
+                                    createGlInterface = true;
+                                    sizeChanged = true;
+                                }
+
+                                if (mHaveEglSurface) {
+                                    if (mSizeChanged) {
+                                        sizeChanged = true;
+                                        w = mWidth;
+                                        h = mHeight;
+                                        wantRenderNotification = true;
+                                        if (LOG_SURFACE) {
+                                            Log.i("GLThread",
+                                                    "noticing that we want render notification tid="
+                                                    + getId());
+                                        }
+
+                                        // Destroy and recreate the EGL surface.
+                                        createEglSurface = true;
+
+                                        mSizeChanged = false;
+                                    }
+                                    mRequestRender = false;
+                                    sGLThreadManager.notifyAll();
+                                    break;
+                                }
+                            }
+
+                            // By design, this is the only place in a GLThread thread where we wait().
+                            if (LOG_THREADS) {
+                                Log.i("GLThread", "waiting tid=" + getId()
+                                    + " mHaveEglContext: " + mHaveEglContext
+                                    + " mHaveEglSurface: " + mHaveEglSurface
+                                    + " mPaused: " + mPaused
+                                    + " mHasSurface: " + mHasSurface
+                                    + " mSurfaceIsBad: " + mSurfaceIsBad
+                                    + " mWaitingForSurface: " + mWaitingForSurface
+                                    + " mWidth: " + mWidth
+                                    + " mHeight: " + mHeight
+                                    + " mRequestRender: " + mRequestRender
+                                    + " mRenderMode: " + mRenderMode);
+                            }
+                            sGLThreadManager.wait();
+                        }
+                    } // end of synchronized(sGLThreadManager)
+
+                    if (event != null) {
+                        event.run();
+                        event = null;
+                        continue;
+                    }
+
+                    if (createEglSurface) {
+                        if (LOG_SURFACE) {
+                            Log.w("GLThread", "egl createSurface");
+                        }
+                        if (!mEglHelper.createSurface()) {
+                            synchronized(sGLThreadManager) {
+                                mSurfaceIsBad = true;
+                                sGLThreadManager.notifyAll();
+                            }
+                            continue;
+                        }
+                        createEglSurface = false;
+                    }
+
+                    if (createGlInterface) {
+                        gl = (GL10) mEglHelper.createGL();
+
+                        sGLThreadManager.checkGLDriver(gl);
+                        createGlInterface = false;
+                    }
+
+                    if (createEglContext) {
+                        if (LOG_RENDERER) {
+                            Log.w("GLThread", "onSurfaceCreated");
+                        }
+                        GLSurfaceViewCustom view = mGLSurfaceViewWeakRef.get();
+                        if (view != null) {
+                            view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
+                        }
+                        createEglContext = false;
+                    }
+
+                    if (sizeChanged) {
+                        if (LOG_RENDERER) {
+                            Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")");
+                        }
+                        GLSurfaceViewCustom view = mGLSurfaceViewWeakRef.get();
+                        if (view != null) {
+                            view.mRenderer.onSurfaceChanged(gl, w, h);
+                        }
+                        sizeChanged = false;
+                    }
+
+                    if (LOG_RENDERER_DRAW_FRAME) {
+                        Log.w("GLThread", "onDrawFrame tid=" + getId());
+                    }
+                    {
+                        GLSurfaceViewCustom view = mGLSurfaceViewWeakRef.get();
+                        if (view != null) {
+                            view.mRenderer.onDrawFrame(gl);
+                        }
+                    }
+                    int swapError = mEglHelper.swap();
+                    switch (swapError) {
+                        case EGL10.EGL_SUCCESS:
+                            GLSurfaceViewCustom view = mGLSurfaceViewWeakRef.get();
+                            if (view != null) {
+                                view.mRenderer.onEglSwapBuffers();
+                            }
+                            break;
+                        case EGL11.EGL_CONTEXT_LOST:
+                            if (LOG_SURFACE) {
+                                Log.i("GLThread", "egl context lost tid=" + getId());
+                            }
+                            lostEglContext = true;
+                            break;
+                        default:
+                            // Other errors typically mean that the current surface is bad,
+                            // probably because the SurfaceView surface has been destroyed,
+                            // but we haven't been notified yet.
+                            // Log the error to help developers understand why rendering stopped.
+                            EglHelper.logEglErrorAsWarning("GLThread", "eglSwapBuffers", swapError);
+
+                            synchronized(sGLThreadManager) {
+                                mSurfaceIsBad = true;
+                                sGLThreadManager.notifyAll();
+                            }
+                            break;
+                    }
+
+                    if (wantRenderNotification) {
+                        doRenderNotification = true;
+                    }
+                }
+
+            } finally {
+                /*
+                 * clean-up everything...
+                 */
+                synchronized (sGLThreadManager) {
+                    stopEglSurfaceLocked();
+                    stopEglContextLocked();
+                }
+            }
+        }
+
+        public boolean ableToDraw() {
+            return mHaveEglContext && mHaveEglSurface && readyToDraw();
+        }
+
+        private boolean readyToDraw() {
+            return (!mPaused) && mHasSurface && (!mSurfaceIsBad)
+                && (mWidth > 0) && (mHeight > 0)
+                && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY));
+        }
+
+        public void setRenderMode(int renderMode) {
+            if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY)) ) {
+                throw new IllegalArgumentException("renderMode");
+            }
+            synchronized(sGLThreadManager) {
+                mRenderMode = renderMode;
+                sGLThreadManager.notifyAll();
+            }
+        }
+
+        public int getRenderMode() {
+            synchronized(sGLThreadManager) {
+                return mRenderMode;
+            }
+        }
+
+        public void requestRender() {
+            synchronized(sGLThreadManager) {
+                mRequestRender = true;
+                sGLThreadManager.notifyAll();
+            }
+        }
+
+        public void surfaceCreated() {
+            synchronized(sGLThreadManager) {
+                if (LOG_THREADS) {
+                    Log.i("GLThread", "surfaceCreated tid=" + getId());
+                }
+                mHasSurface = true;
+                sGLThreadManager.notifyAll();
+                while((mWaitingForSurface) && (!mExited)) {
+                    try {
+                        sGLThreadManager.wait();
+                    } catch (InterruptedException e) {
+                        Thread.currentThread().interrupt();
+                    }
+                }
+            }
+        }
+
+        public void surfaceDestroyed() {
+            synchronized(sGLThreadManager) {
+                if (LOG_THREADS) {
+                    Log.i("GLThread", "surfaceDestroyed tid=" + getId());
+                }
+                mHasSurface = false;
+                sGLThreadManager.notifyAll();
+                while((!mWaitingForSurface) && (!mExited)) {
+                    try {
+                        sGLThreadManager.wait();
+                    } catch (InterruptedException e) {
+                        Thread.currentThread().interrupt();
+                    }
+                }
+            }
+        }
+
+        public void onPause() {
+            synchronized (sGLThreadManager) {
+                if (LOG_PAUSE_RESUME) {
+                    Log.i("GLThread", "onPause tid=" + getId());
+                }
+                mRequestPaused = true;
+                sGLThreadManager.notifyAll();
+                while ((! mExited) && (! mPaused)) {
+                    if (LOG_PAUSE_RESUME) {
+                        Log.i("Main thread", "onPause waiting for mPaused.");
+                    }
+                    try {
+                        sGLThreadManager.wait();
+                    } catch (InterruptedException ex) {
+                        Thread.currentThread().interrupt();
+                    }
+                }
+            }
+        }
+
+        public void onResume() {
+            synchronized (sGLThreadManager) {
+                if (LOG_PAUSE_RESUME) {
+                    Log.i("GLThread", "onResume tid=" + getId());
+                }
+                mRequestPaused = false;
+                mRequestRender = true;
+                mRenderComplete = false;
+                sGLThreadManager.notifyAll();
+                while ((! mExited) && mPaused && (!mRenderComplete)) {
+                    if (LOG_PAUSE_RESUME) {
+                        Log.i("Main thread", "onResume waiting for !mPaused.");
+                    }
+                    try {
+                        sGLThreadManager.wait();
+                    } catch (InterruptedException ex) {
+                        Thread.currentThread().interrupt();
+                    }
+                }
+            }
+        }
+
+        public void onWindowResize(int w, int h) {
+            synchronized (sGLThreadManager) {
+                mWidth = w;
+                mHeight = h;
+                mSizeChanged = true;
+                mRequestRender = true;
+                mRenderComplete = false;
+                sGLThreadManager.notifyAll();
+
+                // Wait for thread to react to resize and render a frame
+                while (! mExited && !mPaused && !mRenderComplete
+                        && ableToDraw()) {
+                    if (LOG_SURFACE) {
+                        Log.i("Main thread", "onWindowResize waiting for render complete from tid=" + getId());
+                    }
+                    try {
+                        sGLThreadManager.wait();
+                    } catch (InterruptedException ex) {
+                        Thread.currentThread().interrupt();
+                    }
+                }
+            }
+        }
+
+        public void requestExitAndWait() {
+            // don't call this from GLThread thread or it is a guaranteed
+            // deadlock!
+            synchronized(sGLThreadManager) {
+                mShouldExit = true;
+                sGLThreadManager.notifyAll();
+                while (! mExited) {
+                    try {
+                        sGLThreadManager.wait();
+                    } catch (InterruptedException ex) {
+                        Thread.currentThread().interrupt();
+                    }
+                }
+            }
+        }
+
+        public void requestReleaseEglContextLocked() {
+            mShouldReleaseEglContext = true;
+            sGLThreadManager.notifyAll();
+        }
+
+        /**
+         * Queue an "event" to be run on the GL rendering thread.
+         * @param r the runnable to be run on the GL rendering thread.
+         */
+        public void queueEvent(Runnable r) {
+            if (r == null) {
+                throw new IllegalArgumentException("r must not be null");
+            }
+            synchronized(sGLThreadManager) {
+                mEventQueue.add(r);
+                sGLThreadManager.notifyAll();
+            }
+        }
+
+        // Once the thread is started, all accesses to the following member
+        // variables are protected by the sGLThreadManager monitor
+        private boolean mShouldExit;
+        private boolean mExited;
+        private boolean mRequestPaused;
+        private boolean mPaused;
+        private boolean mHasSurface;
+        private boolean mSurfaceIsBad;
+        private boolean mWaitingForSurface;
+        private boolean mHaveEglContext;
+        private boolean mHaveEglSurface;
+        private boolean mShouldReleaseEglContext;
+        private int mWidth;
+        private int mHeight;
+        private int mRenderMode;
+        private boolean mRequestRender;
+        private boolean mRenderComplete;
+        private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>();
+        private boolean mSizeChanged = true;
+
+        // End of member variables protected by the sGLThreadManager monitor.
+
+        private EglHelper mEglHelper;
+
+        /**
+         * Set once at thread construction time, nulled out when the parent view is garbage
+         * called. This weak reference allows the GLSurfaceView to be garbage collected while
+         * the GLThread is still alive.
+         */
+        private WeakReference<GLSurfaceViewCustom> mGLSurfaceViewWeakRef;
+
+    }
+
+    static class LogWriter extends Writer {
+
+        @Override public void close() {
+            flushBuilder();
+        }
+
+        @Override public void flush() {
+            flushBuilder();
+        }
+
+        @Override public void write(char[] buf, int offset, int count) {
+            for(int i = 0; i < count; i++) {
+                char c = buf[offset + i];
+                if ( c == '\n') {
+                    flushBuilder();
+                }
+                else {
+                    mBuilder.append(c);
+                }
+            }
+        }
+
+        private void flushBuilder() {
+            if (mBuilder.length() > 0) {
+                Log.v("GLSurfaceViewCustom", mBuilder.toString());
+                mBuilder.delete(0, mBuilder.length());
+            }
+        }
+
+        private StringBuilder mBuilder = new StringBuilder();
+    }
+
+
+    private void checkRenderThreadState() {
+        if (mGLThread != null) {
+            throw new IllegalStateException(
+                    "setRenderer has already been called for this instance.");
+        }
+    }
+
+    private static class GLThreadManager {
+        private static String TAG = "GLThreadManager";
+
+        public synchronized void threadExiting(GLThread thread) {
+            if (LOG_THREADS) {
+                Log.i("GLThread", "exiting tid=" +  thread.getId());
+            }
+            thread.mExited = true;
+            if (mEglOwner == thread) {
+                mEglOwner = null;
+            }
+            notifyAll();
+        }
+
+        /*
+         * Tries once to acquire the right to use an EGL
+         * context. Does not block. Requires that we are already
+         * in the sGLThreadManager monitor when this is called.
+         *
+         * @return true if the right to use an EGL context was acquired.
+         */
+        public boolean tryAcquireEglContextLocked(GLThread thread) {
+            if (mEglOwner == thread || mEglOwner == null) {
+                mEglOwner = thread;
+                notifyAll();
+                return true;
+            }
+            checkGLESVersion();
+            if (mMultipleGLESContextsAllowed) {
+                return true;
+            }
+            // Notify the owning thread that it should release the context.
+            // TODO: implement a fairness policy. Currently
+            // if the owning thread is drawing continuously it will just
+            // reacquire the EGL context.
+            if (mEglOwner != null) {
+                mEglOwner.requestReleaseEglContextLocked();
+            }
+            return false;
+        }
+
+        /*
+         * Releases the EGL context. Requires that we are already in the
+         * sGLThreadManager monitor when this is called.
+         */
+        public void releaseEglContextLocked(GLThread thread) {
+            if (mEglOwner == thread) {
+                mEglOwner = null;
+            }
+            notifyAll();
+        }
+
+        public synchronized boolean shouldReleaseEGLContextWhenPausing() {
+            // Release the EGL context when pausing even if
+            // the hardware supports multiple EGL contexts.
+            // Otherwise the device could run out of EGL contexts.
+            return mLimitedGLESContexts;
+        }
+
+        public synchronized boolean shouldTerminateEGLWhenPausing() {
+            checkGLESVersion();
+            return !mMultipleGLESContextsAllowed;
+        }
+
+        public synchronized void checkGLDriver(GL10 gl) {
+            if (! mGLESDriverCheckComplete) {
+                checkGLESVersion();
+                String renderer = gl.glGetString(GL10.GL_RENDERER);
+                if (mGLESVersion < kGLES_20) {
+                    mMultipleGLESContextsAllowed =
+                        ! renderer.startsWith(kMSM7K_RENDERER_PREFIX);
+                    notifyAll();
+                }
+                mLimitedGLESContexts = !mMultipleGLESContextsAllowed;
+                if (LOG_SURFACE) {
+                    Log.w(TAG, "checkGLDriver renderer = \"" + renderer + "\" multipleContextsAllowed = "
+                        + mMultipleGLESContextsAllowed
+                        + " mLimitedGLESContexts = " + mLimitedGLESContexts);
+                }
+                mGLESDriverCheckComplete = true;
+            }
+        }
+
+        private void checkGLESVersion() {
+            if (! mGLESVersionCheckComplete) {
+                mGLESVersion = kGLES_20; /*SystemProperties.getInt(
+                        "ro.opengles.version",
+                        ConfigurationInfo.GL_ES_VERSION_UNDEFINED); */
+                if (mGLESVersion >= kGLES_20) {
+                    mMultipleGLESContextsAllowed = true;
+                }
+                if (LOG_SURFACE) {
+                    Log.w(TAG, "checkGLESVersion mGLESVersion =" +
+                            " " + mGLESVersion + " mMultipleGLESContextsAllowed = " + mMultipleGLESContextsAllowed);
+                }
+                mGLESVersionCheckComplete = true;
+            }
+        }
+
+        /**
+         * This check was required for some pre-Android-3.0 hardware. Android 3.0 provides
+         * support for hardware-accelerated views, therefore multiple EGL contexts are
+         * supported on all Android 3.0+ EGL drivers.
+         */
+        private boolean mGLESVersionCheckComplete;
+        private int mGLESVersion;
+        private boolean mGLESDriverCheckComplete;
+        private boolean mMultipleGLESContextsAllowed;
+        private boolean mLimitedGLESContexts;
+        private static final int kGLES_20 = 0x20000;
+        private static final String kMSM7K_RENDERER_PREFIX =
+            "Q3Dimension MSM7500 ";
+        private GLThread mEglOwner;
+    }
+
+    private static final GLThreadManager sGLThreadManager = new GLThreadManager();
+
+    private final WeakReference<GLSurfaceViewCustom> mThisWeakRef =
+            new WeakReference<GLSurfaceViewCustom>(this);
+    private GLThread mGLThread;
+    private Renderer mRenderer;
+    private boolean mDetached;
+    private EGLConfigChooser mEGLConfigChooser;
+    private EGLContextFactory mEGLContextFactory;
+    private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory;
+    private GLWrapper mGLWrapper;
+    private int mDebugFlags;
+    private int mEGLContextClientVersion;
+    private boolean mPreserveEGLContextOnPause;
+}
diff --git a/tests/tests/openglperf/src/android/openglperf/cts/GlPlanetsActivity.java b/tests/tests/openglperf/src/android/openglperf/cts/GlPlanetsActivity.java
index 5a8c0a8..7aa1d21 100644
--- a/tests/tests/openglperf/src/android/openglperf/cts/GlPlanetsActivity.java
+++ b/tests/tests/openglperf/src/android/openglperf/cts/GlPlanetsActivity.java
@@ -34,14 +34,15 @@
     public static final String INTENT_EXTRA_USE_VBO_VERTICES = "useVboVertices";
     public static final String INTENT_EXTRA_USE_VBO_INDICES = "useVboIndiices";
     public static final String INTENT_EXTRA_NUM_FRAMES = "numFrames";
-    public static final String INTENT_EXTRA_INDICES_PER_VERTEX = "numIndicesPerVertex";
+    public static final String INTENT_EXTRA_NUM_INDEX_BUFFERS = "numIndexBuffers";
 
     public static final String INTENT_RESULT_FPS = "fps";
     public static final String INTENT_RESULT_NUM_TRIANGLES = "numTrigngles";
 
     private final Semaphore mSem = new Semaphore(0);
-    private float mFps;
+    private float mAverageFps;
     private int mNumTriangles;
+    private int[] mFrameInterval;
 
     private PlanetsSurfaceView mView;
 
@@ -50,14 +51,23 @@
         return mSem.tryAcquire(timeoutInSecs, TimeUnit.SECONDS);
     }
 
-    public float getFps() {
-        return mFps;
+    public float getAverageFps() {
+        return mAverageFps;
     }
 
     public int getNumTriangles() {
         return mNumTriangles;
     }
 
+    /**
+     * Time interval between each frame's rendering in ms.
+     * The first value will be invalid, so client should discard them.
+     * @return can return null if INTENT_EXTRA_NUM_FRAMES was not set in intent.
+     */
+    public int[] getFrameInterval() {
+        return mFrameInterval;
+    }
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -70,16 +80,17 @@
         param.mUseVboForIndices = intent.getBooleanExtra(INTENT_EXTRA_USE_VBO_INDICES,
                 param.mUseVboForIndices);
         param.mNumFrames = intent.getIntExtra(INTENT_EXTRA_NUM_FRAMES, param.mNumFrames);
-        param.mNumIndicesPerVertex = intent.getIntExtra(INTENT_EXTRA_INDICES_PER_VERTEX,
+        param.mNumIndicesPerVertex = intent.getIntExtra(INTENT_EXTRA_NUM_INDEX_BUFFERS,
                 param.mNumIndicesPerVertex);
         mView = new PlanetsSurfaceView(this, param, this);
         setContentView(mView);
     }
 
     @Override
-    public void onRenderCompletion(float fps, int numTriangles) {
-        mFps = fps;
+    public void onRenderCompletion(float averageFps, int numTriangles,  int[] frameInterval) {
+        mAverageFps = averageFps;
         mNumTriangles = numTriangles;
+        mFrameInterval = frameInterval;
         mSem.release();
     }
 
diff --git a/tests/tests/openglperf/src/android/openglperf/cts/GlVboPerfTest.java b/tests/tests/openglperf/src/android/openglperf/cts/GlVboPerfTest.java
index afd435c..2e9e10b 100644
--- a/tests/tests/openglperf/src/android/openglperf/cts/GlVboPerfTest.java
+++ b/tests/tests/openglperf/src/android/openglperf/cts/GlVboPerfTest.java
@@ -87,14 +87,14 @@
     }
 
     private void runRendering(int numPlanets, boolean useVboVertex, boolean useVboIndex,
-            int indicesPerVertex) throws Exception {
+            int numIndexBuffers) throws Exception {
         Intent intent = new Intent();
         intent.putExtra(GlPlanetsActivity.INTENT_EXTRA_NUM_FRAMES,
                 NUM_FRAMES_TO_RENDER);
         intent.putExtra(GlPlanetsActivity.INTENT_EXTRA_NUM_PLANETS, numPlanets);
         intent.putExtra(GlPlanetsActivity.INTENT_EXTRA_USE_VBO_VERTICES, useVboVertex);
         intent.putExtra(GlPlanetsActivity.INTENT_EXTRA_USE_VBO_INDICES, useVboIndex);
-        intent.putExtra(GlPlanetsActivity.INTENT_EXTRA_INDICES_PER_VERTEX, indicesPerVertex);
+        intent.putExtra(GlPlanetsActivity.INTENT_EXTRA_NUM_INDEX_BUFFERS, numIndexBuffers);
 
         setActivityIntent(intent);
         final GlPlanetsActivity activity = getActivity();
@@ -102,7 +102,7 @@
                 .waitForGlPlanetsCompletionWithTimeout(RENDERING_TIMEOUT);
         assertTrue("timeout while waiting for rendering completion", waitResult);
 
-        mFps = activity.getFps();
+        mFps = activity.getAverageFps();
         mNumTriangles = activity.getNumTriangles();
 
         cleanUpActivity();
diff --git a/tests/tests/openglperf/src/android/openglperf/cts/OpenGlPerfNative.java b/tests/tests/openglperf/src/android/openglperf/cts/OpenGlPerfNative.java
new file mode 100644
index 0000000..d63a640
--- /dev/null
+++ b/tests/tests/openglperf/src/android/openglperf/cts/OpenGlPerfNative.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.openglperf.cts;
+
+public class OpenGlPerfNative {
+    static {
+        System.loadLibrary("ctsopenglperf_jni");
+    }
+    /**
+     * wait for the completion of the pending Egl/Gl operation using eglClientWaitSyncKHR.
+     * @param waitTimeInNs maximum time to wait in nano secs
+     * @return true for success, false for timeout
+     */
+    public static native boolean waitForEglCompletion(long waitTimeInNs);
+}
diff --git a/tests/tests/openglperf/src/android/openglperf/cts/PlanetsRenderer.java b/tests/tests/openglperf/src/android/openglperf/cts/PlanetsRenderer.java
index 5b90089..b286e46 100644
--- a/tests/tests/openglperf/src/android/openglperf/cts/PlanetsRenderer.java
+++ b/tests/tests/openglperf/src/android/openglperf/cts/PlanetsRenderer.java
@@ -17,10 +17,10 @@
 package android.openglperf.cts;
 
 import android.content.Context;
+import android.cts.util.WatchDog;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.opengl.GLES20;
-import android.opengl.GLSurfaceView;
 import android.opengl.GLUtils;
 import android.opengl.Matrix;
 import android.util.Log;
@@ -34,18 +34,21 @@
 import javax.microedition.khronos.egl.EGLConfig;
 import javax.microedition.khronos.opengles.GL10;
 
-
-public class PlanetsRenderer implements GLSurfaceView.Renderer {
+/**
+ * OpenGl renderer rendering given number of planets with different GL configuration.
+ */
+public class PlanetsRenderer implements GLSurfaceViewCustom.Renderer {
 
     private static final String TAG = "PlanetsRenderer";
     // texture is from
     // http://en.wikipedia.org/wiki/File:Mercator_projection_SW.jpg
     private static final String TEXTURE_FILE = "world_512_512.jpg";
+    private static final long EGL_SWAP_BUFFERS_WAIT_TIME_IN_NS = 100 * 1000 * 1000 * 1000L;
 
     private final Context mContext;
     private final PlanetsRenderingParam mParam;
     private final RenderCompletionListener mListener;
-    private final RenderingWatchDog mWatchDog;
+    private final WatchDog mWatchDog;
 
     private final Sphere[] mSpheres;
     private final int mNumSpheres;
@@ -65,9 +68,11 @@
     private int mFrameCount = 0;
     private static final int FPS_DISPLAY_INTERVAL = 50;
     private long mLastFPSTime;
+    private long mLastRenderingTime;
     // for total FPS measurement
     private long mRenderingStartTime;
     private long mMeasurementStartTime;
+    private int[] mFrameInterval = null;
 
     private int mProgram; // shader program
     private int mMVPMatrixHandle;
@@ -95,7 +100,7 @@
      * @param listener
      */
     public PlanetsRenderer(Context context, PlanetsRenderingParam param,
-            RenderCompletionListener listener, RenderingWatchDog watchDog) {
+            RenderCompletionListener listener, WatchDog watchDog) {
         resetTimer();
         mContext = context;
         mParam = param;
@@ -104,6 +109,9 @@
         mNumIndices = mNumSpheres * mParam.mNumIndicesPerVertex;
         mSpheres = new Sphere[mNumSpheres];
 
+        if (mParam.mNumFrames > 0) {
+            mFrameInterval = new int[mParam.mNumFrames];
+        }
         printParams();
 
         // for big model, this construction phase takes time...
@@ -121,6 +129,7 @@
         measureTime("construction");
     }
 
+    @Override
     public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
         mProgram = createProgram(getVertexShader(), getFragmentShader());
         if (mProgram == 0) {
@@ -137,31 +146,12 @@
         mTextureId = createTexture2D();
     }
 
+    @Override
     public void onDrawFrame(GL10 glUnused) {
         mWatchDog.reset();
         long currentTime = System.currentTimeMillis();
-
         mFrameCount++;
-        if ((mFrameCount % FPS_DISPLAY_INTERVAL == 0) && (mFrameCount != 0)) {
-            float fps = (((float) FPS_DISPLAY_INTERVAL)
-                    / ((float) (currentTime - mLastFPSTime)) * 1000.0f);
-            // FPS is not correct if activity is paused/resumed.
-            Log.i(TAG, "FPS " + fps);
-            mLastFPSTime = currentTime;
-        }
-
-        if ((mFrameCount == mParam.mNumFrames) && (mParam.mNumFrames > 0)) {
-            long timePassed = currentTime - mRenderingStartTime;
-            float fps = ((float) mParam.mNumFrames) / ((float) timePassed) * 1000.0f;
-            printGlInfos();
-            printParams();
-            int numTriangles = mNumSpheres * mSpheres[0].getTotalIndices() / 3;
-            Log.i(TAG, "Final FPS " + fps + " Num triangles " + numTriangles);
-            if (mListener != null) {
-                mListener.onRenderCompletion(fps, numTriangles);
-                return;
-            }
-        }
+        mLastRenderingTime = currentTime;
 
         float angle = 0.090f * ((int) (currentTime % 4000L));
         Matrix.setRotateM(mMMatrix, 0, angle, 0, 0, 1.0f);
@@ -232,6 +222,36 @@
         }
     }
 
+    @Override
+    public void onEglSwapBuffers() {
+        if (!OpenGlPerfNative.waitForEglCompletion(EGL_SWAP_BUFFERS_WAIT_TIME_IN_NS)) {
+            Log.w(TAG, "time-out or error while waiting for eglSwapBuffers completion");
+        }
+        long currentTime = System.currentTimeMillis();
+        if (mFrameCount == 0) {
+            mRenderingStartTime = currentTime;
+        }
+        if (mFrameCount < mParam.mNumFrames) {
+            mFrameInterval[mFrameCount] = (int)(currentTime - mLastRenderingTime);
+        }
+
+        if ((mFrameCount == mParam.mNumFrames) && (mParam.mNumFrames > 0)) {
+            long timePassed = currentTime - mRenderingStartTime;
+            float fps = ((float) mParam.mNumFrames) / ((float) timePassed) * 1000.0f;
+            printGlInfos();
+            printParams();
+            int numTriangles = mNumSpheres * mSpheres[0].getTotalIndices() / 3;
+            Log.i(TAG, "Final FPS " + fps + " Num triangles " + numTriangles + " start time " +
+                    mRenderingStartTime + " finish time " + currentTime);
+            if (mListener != null) {
+                mListener.onRenderCompletion(fps, numTriangles, mFrameInterval);
+                mFrameCount++; // to prevent entering here again
+                return;
+            }
+        }
+    }
+
+    @Override
     public void onSurfaceChanged(GL10 glUnused, int width, int height) {
         mWidth = width;
         mHeight = height;
diff --git a/tests/tests/openglperf/src/android/openglperf/cts/PlanetsSurfaceView.java b/tests/tests/openglperf/src/android/openglperf/cts/PlanetsSurfaceView.java
index 2bfd001..072988d 100644
--- a/tests/tests/openglperf/src/android/openglperf/cts/PlanetsSurfaceView.java
+++ b/tests/tests/openglperf/src/android/openglperf/cts/PlanetsSurfaceView.java
@@ -17,11 +17,11 @@
 package android.openglperf.cts;
 
 import android.content.Context;
-import android.opengl.GLSurfaceView;
+import android.cts.util.WatchDog;
 
-class PlanetsSurfaceView extends GLSurfaceView {
+class PlanetsSurfaceView extends GLSurfaceViewCustom {
     private final long RENDERING_TIMEOUT = 1900; // in msec, close to 2 secs
-    private final RenderingWatchDog mWatchDog = new RenderingWatchDog(RENDERING_TIMEOUT);
+    private final WatchDog mWatchDog = new WatchDog(RENDERING_TIMEOUT);
 
     public PlanetsSurfaceView(Context context, PlanetsRenderingParam param,
             RenderCompletionListener listener) {
@@ -44,4 +44,4 @@
         setRenderMode(RENDERMODE_CONTINUOUSLY);
         super.onResume();
     }
-}
\ No newline at end of file
+}
diff --git a/tests/tests/openglperf/src/android/openglperf/cts/RenderCompletionListener.java b/tests/tests/openglperf/src/android/openglperf/cts/RenderCompletionListener.java
index 1643087..a5bbfa2 100644
--- a/tests/tests/openglperf/src/android/openglperf/cts/RenderCompletionListener.java
+++ b/tests/tests/openglperf/src/android/openglperf/cts/RenderCompletionListener.java
@@ -16,11 +16,15 @@
 
 package android.openglperf.cts;
 
+/**
+ * Interface used to notify the completion of requested rendering.
+ */
 public interface RenderCompletionListener {
     /**
-     * @param fps total frame rate
+     * @param averageFps average of total frames
      * @param numTriangles Number of triangles in geometric model
+     * @param frameInterval interval for each frame in ms. Do not use the first one and the last one.
      */
-    void onRenderCompletion(float fps, int numTriangles);
+    void onRenderCompletion(float averageFps, int numTriangles, int[] frameInterval);
 
 }
diff --git a/tests/tests/openglperf/src/android/openglperf/cts/Sphere.java b/tests/tests/openglperf/src/android/openglperf/cts/Sphere.java
index 026ba97..8020db6 100644
--- a/tests/tests/openglperf/src/android/openglperf/cts/Sphere.java
+++ b/tests/tests/openglperf/src/android/openglperf/cts/Sphere.java
@@ -49,7 +49,7 @@
      * @param x,y,z the origin of the sphere
      * @param r the radius of the sphere
      */
-    public Sphere(int nSlices, float x, float y, float z, float r, int indicesPerVertex) {
+    public Sphere(int nSlices, float x, float y, float z, float r, int numIndexBuffers) {
 
         int iMax = nSlices + 1;
         int nVertices = iMax * iMax;
@@ -64,17 +64,17 @@
         // 3 vertex coords + 2 texture coords
         mVertices = ByteBuffer.allocateDirect(nVertices * 5 * FLOAT_SIZE)
                 .order(ByteOrder.nativeOrder()).asFloatBuffer();
-        mIndices = new ShortBuffer[indicesPerVertex];
-        mNumIndices = new int[indicesPerVertex];
+        mIndices = new ShortBuffer[numIndexBuffers];
+        mNumIndices = new int[numIndexBuffers];
         // first evenly distribute to n-1 buffers, then put remaining ones to the last one.
-        int noIndicesPerBuffer = (mTotalIndices / indicesPerVertex / 6) * 6;
-        for (int i = 0; i < indicesPerVertex - 1; i++) {
+        int noIndicesPerBuffer = (mTotalIndices / numIndexBuffers / 6) * 6;
+        for (int i = 0; i < numIndexBuffers - 1; i++) {
             mNumIndices[i] = noIndicesPerBuffer;
         }
-        mNumIndices[indicesPerVertex - 1] = mTotalIndices - noIndicesPerBuffer *
-                (indicesPerVertex - 1);
+        mNumIndices[numIndexBuffers - 1] = mTotalIndices - noIndicesPerBuffer *
+                (numIndexBuffers - 1);
 
-        for (int i = 0; i < indicesPerVertex; i++) {
+        for (int i = 0; i < numIndexBuffers; i++) {
             mIndices[i] = ByteBuffer.allocateDirect(mNumIndices[i] * SHORT_SIZE)
                     .order(ByteOrder.nativeOrder()).asShortBuffer();
         }
@@ -123,7 +123,7 @@
         mIndices[bufferNum].put(indexBuffer, 0, mNumIndices[bufferNum]);
 
         mVertices.position(0);
-        for (int i = 0; i < indicesPerVertex; i++) {
+        for (int i = 0; i < numIndexBuffers; i++) {
             mIndices[i].position(0);
         }
     }
diff --git a/tests/tests/os/src/android/os/cts/WorkSourceTest.java b/tests/tests/os/src/android/os/cts/WorkSourceTest.java
new file mode 100644
index 0000000..f3986b1
--- /dev/null
+++ b/tests/tests/os/src/android/os/cts/WorkSourceTest.java
@@ -0,0 +1,557 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.os.cts;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import android.os.WorkSource;
+import android.test.AndroidTestCase;
+
+public class WorkSourceTest extends AndroidTestCase {
+    private Constructor<WorkSource> mConstructWS;
+    private Object[] mConstructWSArgs = new Object[1];
+    private Method mAddUid;
+    private Object[] mAddUidArgs = new Object[1];
+    private Method mAddUidName;
+    private Object[] mAddUidNameArgs = new Object[2];
+    private Method mAddReturningNewbs;
+    private Object[] mAddReturningNewbsArgs = new Object[1];
+    private Method mSetReturningDiffs;
+    private Object[] mSetReturningDiffsArgs = new Object[1];
+    private Method mStripNames;
+    private Object[] mStripNamesArgs = new Object[0];
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mConstructWS = WorkSource.class.getConstructor(new Class[] { int.class });
+        mAddUid = WorkSource.class.getMethod("add", new Class[] { int.class });
+        mAddUidName = WorkSource.class.getMethod("add", new Class[] { int.class, String.class });
+        mAddReturningNewbs = WorkSource.class.getMethod("addReturningNewbs", new Class[] { WorkSource.class });
+        mSetReturningDiffs = WorkSource.class.getMethod("setReturningDiffs", new Class[] { WorkSource.class });
+        mStripNames = WorkSource.class.getMethod("stripNames", new Class[] {  });
+    }
+
+    private WorkSource wsNew(int uid) throws IllegalArgumentException,
+            InstantiationException, IllegalAccessException, InvocationTargetException {
+        mConstructWSArgs[0] = uid;
+        return mConstructWS.newInstance(mConstructWSArgs);
+    }
+
+    private WorkSource wsNew(int[] uids) throws IllegalArgumentException,
+            InstantiationException, IllegalAccessException, InvocationTargetException {
+        WorkSource ws = new WorkSource();
+        for (int i=0; i<uids.length; i++) {
+            wsAdd(ws, uids[i]);
+        }
+        checkWorkSource("Constructed", ws, uids);
+        return ws;
+    }
+
+    private WorkSource wsNew(int[] uids, String[] names) throws IllegalArgumentException,
+            InstantiationException, IllegalAccessException, InvocationTargetException {
+        WorkSource ws = new WorkSource();
+        for (int i=0; i<uids.length; i++) {
+            wsAdd(ws, uids[i], names[i]);
+        }
+        checkWorkSource("Constructed", ws, uids, names);
+        return ws;
+    }
+
+    private boolean wsAdd(WorkSource ws, int uid) throws IllegalArgumentException,
+            InstantiationException, IllegalAccessException, InvocationTargetException {
+        mAddUidArgs[0] = uid;
+        return (Boolean)mAddUid.invoke(ws, mAddUidArgs);
+    }
+
+    private boolean wsAdd(WorkSource ws, int uid, String name) throws IllegalArgumentException,
+            InstantiationException, IllegalAccessException, InvocationTargetException {
+        mAddUidNameArgs[0] = uid;
+        mAddUidNameArgs[1] = name;
+        return (Boolean)mAddUidName.invoke(ws, mAddUidNameArgs);
+    }
+
+    private WorkSource wsAddReturningNewbs(WorkSource ws, WorkSource other) throws IllegalArgumentException,
+            InstantiationException, IllegalAccessException, InvocationTargetException {
+        mAddReturningNewbsArgs[0] = other;
+        return (WorkSource)mAddReturningNewbs.invoke(ws, mAddReturningNewbsArgs);
+    }
+
+    private WorkSource[] wsSetReturningDiffs(WorkSource ws, WorkSource other) throws IllegalArgumentException,
+            InstantiationException, IllegalAccessException, InvocationTargetException {
+        mSetReturningDiffsArgs[0] = other;
+        return (WorkSource[])mSetReturningDiffs.invoke(ws, mSetReturningDiffsArgs);
+    }
+
+    private WorkSource wsStripNames(WorkSource ws) throws IllegalArgumentException,
+            InstantiationException, IllegalAccessException, InvocationTargetException {
+        return (WorkSource)mStripNames.invoke(ws);
+    }
+
+    private void printArrays(StringBuilder sb, int[] uids, String[] names) {
+        sb.append("{ ");
+        for (int i=0; i<uids.length; i++) {
+            if (i > 0) sb.append(", ");
+            sb.append(uids[i]);
+            if (names != null) {
+                sb.append(" ");
+                sb.append(names[i]);
+            }
+        }
+        sb.append(" }");
+    }
+
+    private void failWorkSource(String op, WorkSource ws, int[] uids) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(op);
+        sb.append(": Expected: ");
+        printArrays(sb, uids, null);
+        sb.append(", got: ");
+        sb.append(ws);
+        fail(sb.toString());
+    }
+
+    private void failWorkSource(String op, WorkSource ws, int[] uids, String[] names) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(op);
+        sb.append(": Expected: ");
+        printArrays(sb, uids, names);
+        sb.append(", got: ");
+        sb.append(ws);
+        fail(sb.toString());
+    }
+
+    private void checkWorkSource(String op, WorkSource ws, int[] uids) {
+        if (ws == null || uids == null) {
+            if (ws != null) {
+                fail(op + ": WorkSource is not null " + ws +", but expected null");
+            }
+            if (uids != null) {
+                fail(op + "WorkSource is null, but expected non-null: " + uids);
+            }
+            return;
+        }
+        if (ws.size() != uids.length) {
+            failWorkSource(op, ws, uids);
+        }
+        for (int i=0; i<uids.length; i++) {
+            if (uids[i] != ws.get(i)) {
+                failWorkSource(op, ws, uids);
+            }
+        }
+    }
+
+    private void checkWorkSource(String op, WorkSource ws, int[] uids, String[] names) {
+        if (ws == null || uids == null) {
+            if (ws != null) {
+                fail(op + ": WorkSource is not null " + ws +", but expected null");
+            }
+            if (uids != null) {
+                fail(op + "WorkSource is null, but expected non-null: " + uids);
+            }
+            return;
+        }
+        if (ws.size() != uids.length) {
+            failWorkSource(op, ws, uids, names);
+        }
+        for (int i=0; i<uids.length; i++) {
+            if (uids[i] != ws.get(i) || !names[i].equals(ws.getName(i))) {
+                failWorkSource(op, ws, uids, names);
+            }
+        }
+    }
+
+    public void testConstructEmpty() {
+        checkWorkSource("Empty", new WorkSource(), new int[] { });
+    }
+
+    public void testConstructSingle() throws Exception {
+        checkWorkSource("Single 1", wsNew(1), new int[] { 1 });
+    }
+
+    public void testAddRawOrdered() throws Exception {
+        WorkSource ws = wsNew(1);
+        wsAdd(ws, 2);
+        checkWorkSource("First", ws, new int[] { 1 , 2 });
+        wsAdd(ws, 20);
+        checkWorkSource("Second", ws, new int[] { 1 , 2, 20 });
+        wsAdd(ws, 100);
+        checkWorkSource("Third", ws, new int[] { 1, 2, 20, 100 });
+    }
+
+    public void testAddRawRevOrdered() throws Exception {
+        WorkSource ws = wsNew(100);
+        wsAdd(ws, 20);
+        checkWorkSource("First", ws, new int[] { 20, 100 });
+        wsAdd(ws, 2);
+        checkWorkSource("Second", ws, new int[] { 2, 20, 100 });
+        wsAdd(ws, 1);
+        checkWorkSource("Third", ws, new int[] { 1, 2, 20, 100 });
+    }
+
+    public void testAddRawUnordered() throws Exception {
+        WorkSource ws = wsNew(10);
+        wsAdd(ws, 2);
+        checkWorkSource("First", ws, new int[] { 2, 10 });
+        wsAdd(ws, 5);
+        checkWorkSource("Second", ws, new int[] { 2, 5, 10 });
+        wsAdd(ws, 1);
+        checkWorkSource("Third", ws, new int[] { 1, 2, 5, 10 });
+        wsAdd(ws, 100);
+        checkWorkSource("Fourth", ws, new int[] { 1, 2, 5, 10, 100 });
+    }
+
+    public void testAddWsOrdered() throws Exception {
+        WorkSource ws = wsNew(1);
+        ws.add(wsNew(2));
+        checkWorkSource("First", ws, new int[] { 1 , 2 });
+        ws.add(wsNew(20));
+        checkWorkSource("Second", ws, new int[] { 1 , 2, 20 });
+        ws.add(wsNew(100));
+        checkWorkSource("Third", ws, new int[] { 1 , 2, 20, 100 });
+    }
+
+    public void testAddWsRevOrdered() throws Exception {
+        WorkSource ws = wsNew(100);
+        ws.add(wsNew(20));
+        checkWorkSource("First", ws, new int[] { 20, 100 });
+        ws.add(wsNew(2));
+        checkWorkSource("Second", ws, new int[] { 2, 20, 100 });
+        ws.add(wsNew(1));
+        checkWorkSource("Third", ws, new int[] { 1, 2, 20, 100 });
+    }
+
+    public void testAddWsUnordered() throws Exception {
+        WorkSource ws = wsNew(10);
+        ws.add(wsNew(2));
+        checkWorkSource("First", ws, new int[] { 2, 10 });
+        ws.add(wsNew(5));
+        checkWorkSource("Second", ws, new int[] { 2, 5, 10 });
+        ws.add(wsNew(1));
+        checkWorkSource("Third", ws, new int[] { 1, 2, 5, 10 });
+        ws.add(wsNew(100));
+        checkWorkSource("Fourth", ws, new int[] { 1, 2, 5, 10, 100 });
+    }
+
+    private void doTestCombineTwoUids(int[] lhs, int[] rhs, int[] expected, int[] newbs,
+            int[] gones) throws Exception {
+        WorkSource ws1 = wsNew(lhs);
+        WorkSource ws2 = wsNew(rhs);
+        ws1.add(ws2);
+        checkWorkSource("Add result", ws1, expected);
+        ws1 = wsNew(lhs);
+        WorkSource wsNewbs = wsAddReturningNewbs(ws1, ws2);
+        checkWorkSource("AddReturning result", ws1, expected);
+        checkWorkSource("Newbs", wsNewbs, newbs);
+        ws1 = wsNew(lhs);
+        WorkSource[] res = wsSetReturningDiffs(ws1, ws2);
+        checkWorkSource("SetReturning result", ws1, rhs);
+        checkWorkSource("Newbs", res[0], newbs);
+        checkWorkSource("Gones", res[1], gones);
+    }
+
+    private int[] makeRepeatingIntArray(String[] stringarray, int value) {
+        if (stringarray == null) {
+            return null;
+        }
+        int[] res = new int[stringarray.length];
+        for (int i=0; i<stringarray.length; i++) {
+            res[i] = value;
+        }
+        return res;
+    }
+
+    private void doTestCombineTwoNames(String[] lhsnames, String[] rhsnames,
+            String[] expectednames, String[] newbnames,
+            String[] gonenames) throws Exception {
+        int[] lhs = makeRepeatingIntArray(lhsnames, 0);
+        int[] rhs = makeRepeatingIntArray(rhsnames, 0);
+        int[] expected = makeRepeatingIntArray(expectednames, 0);
+        int[] newbs = makeRepeatingIntArray(newbnames, 0);
+        int[] gones = makeRepeatingIntArray(gonenames, 0);
+        doTestCombineTwoUidsNames(lhs, lhsnames, rhs, rhsnames, expected, expectednames,
+                newbs, newbnames, gones, gonenames);
+    }
+
+    private void doTestCombineTwoUidsNames(int[] lhs, String[] lhsnames, int[] rhs, String[] rhsnames,
+            int[] expected, String[] expectednames, int[] newbs, String[] newbnames,
+            int[] gones, String[] gonenames) throws Exception {
+        WorkSource ws1 = wsNew(lhs, lhsnames);
+        WorkSource ws2 = wsNew(rhs, rhsnames);
+        ws1.add(ws2);
+        checkWorkSource("Add result", ws1, expected, expectednames);
+        ws1 = wsNew(lhs, lhsnames);
+        WorkSource wsNewbs = wsAddReturningNewbs(ws1, ws2);
+        checkWorkSource("AddReturning result", ws1, expected, expectednames);
+        checkWorkSource("Newbs", wsNewbs, newbs, newbnames);
+        ws1 = wsNew(lhs, lhsnames);
+        WorkSource[] res = wsSetReturningDiffs(ws1, ws2);
+        checkWorkSource("SetReturning result", ws1, rhs, rhsnames);
+        checkWorkSource("Newbs", res[0], newbs, newbnames);
+        checkWorkSource("Gones", res[1], gones, gonenames);
+    }
+
+    private String[] makeRepeatingStringArray(int[] intarray, String value) {
+        if (intarray == null) {
+            return null;
+        }
+        String[] res = new String[intarray.length];
+        for (int i=0; i<intarray.length; i++) {
+            res[i] = value;
+        }
+        return res;
+    }
+
+    private String[] makeStringArray(int[] intarray) {
+        if (intarray == null) {
+            return null;
+        }
+        String[] res = new String[intarray.length];
+        for (int i=0; i<intarray.length; i++) {
+            res[i] = Character.toString((char)('A' + intarray[i]));
+        }
+        return res;
+    }
+
+    private void doTestCombineTwo(int[] lhs, int[] rhs, int[] expected, int[] newbs,
+            int[] gones) throws Exception {
+        doTestCombineTwoUids(lhs, rhs, expected, newbs, gones);
+        doTestCombineTwoUidsNames(lhs, makeRepeatingStringArray(lhs, "A"),
+                rhs, makeRepeatingStringArray(rhs, "A"),
+                expected, makeRepeatingStringArray(expected, "A"),
+                newbs, makeRepeatingStringArray(newbs, "A"),
+                gones, makeRepeatingStringArray(gones, "A"));
+        doTestCombineTwoNames(makeStringArray(lhs), makeStringArray(rhs),
+                makeStringArray(expected), makeStringArray(newbs), makeStringArray(gones));
+    }
+
+    public void testCombineMultiFront() throws Exception {
+        doTestCombineTwo(
+                new int[] { 10, 20, 30, 40 },
+                new int[] { 1, 2, 15, 16 },
+                new int[] { 1, 2, 10, 15, 16, 20, 30, 40 },
+                new int[] { 1, 2, 15, 16 },
+                new int[] { 10, 20, 30, 40 });
+    }
+
+    public void testCombineMultiMiddle() throws Exception {
+        doTestCombineTwo(
+                new int[] { 10, 20, 30, 40 },
+                new int[] { 12, 14, 22 },
+                new int[] { 10, 12, 14, 20, 22, 30, 40 },
+                new int[] { 12, 14, 22 },
+                new int[] { 10, 20, 30, 40 });
+    }
+
+    public void testCombineMultiEnd() throws Exception {
+        doTestCombineTwo(
+                new int[] { 10, 20, 30, 40 },
+                new int[] { 35, 45, 50 },
+                new int[] { 10, 20, 30, 35, 40, 45, 50 },
+                new int[] { 35, 45, 50 },
+                new int[] { 10, 20, 30, 40 });
+    }
+
+    public void testCombineMultiFull() throws Exception {
+        doTestCombineTwo(
+                new int[] { 10, 20, 30, 40 },
+                new int[] { 1, 2, 15, 35, 50 },
+                new int[] { 1, 2, 10, 15, 20, 30, 35, 40, 50 },
+                new int[] { 1, 2, 15, 35, 50 },
+                new int[] { 10, 20, 30, 40 });
+    }
+
+    public void testCombineMultiSame() throws Exception {
+        doTestCombineTwo(
+                new int[] { 10, 20, 30, 40 },
+                new int[] { 10, 20, 30 },
+                new int[] { 10, 20, 30, 40 },
+                null,
+                new int[] { 40 });
+    }
+
+    public void testCombineMultiSomeSame() throws Exception {
+        doTestCombineTwo(
+                new int[] { 10, 20, 30, 40 },
+                new int[] { 1, 30, 40 },
+                new int[] { 1, 10, 20, 30, 40 },
+                new int[] { 1 },
+                new int[] { 10, 20 });
+    }
+
+    public void testCombineMultiSomeSameUidsNames() throws Exception {
+        doTestCombineTwoUidsNames(
+                new int[]    { 10,  10,  20,  30,  30,  30,  40 },
+                new String[] { "A", "B", "A", "A", "B", "C", "A" },
+                new int[]    { 1,   30,  40,  50 },
+                new String[] { "A", "A", "B", "A" },
+                new int[]    { 1,   10,  10,  20,  30,  30,  30,  40,  40,  50 },
+                new String[] { "A", "A", "B", "A", "A", "B", "C", "A", "B", "A" },
+                new int[]    { 1,   40,  50 },
+                new String[] { "A", "B", "A" },
+                new int[]    { 10,  10,  20,  30,  30,  40 },
+                new String[] { "A", "B", "A", "B", "C", "A" });
+    }
+
+    private void doTestRemoveUids(int[] lhs, int[] rhs, int[] result, boolean diff) throws Exception {
+        WorkSource ws1 = wsNew(lhs);
+        WorkSource ws2 = wsNew(rhs);
+        boolean diffres = ws1.remove(ws2);
+        if (diffres != diff) {
+            StringBuilder sb = new StringBuilder();
+            sb.append("Expected diff ");
+            sb.append(diff);
+            sb.append(" but got ");
+            sb.append(diffres);
+            sb.append(" when removing ");
+            sb.append(ws2);
+            sb.append(" from ");
+            sb.append(ws1);
+            fail(sb.toString());
+        }
+        checkWorkSource("Remove", ws1, result);
+    }
+
+    private void doTestRemoveNames(String[] lhsnames, String[] rhsnames,
+            String[] resultnames, boolean diff) throws Exception {
+        int[] lhs = makeRepeatingIntArray(lhsnames, 0);
+        int[] rhs = makeRepeatingIntArray(rhsnames, 0);
+        int[] result = makeRepeatingIntArray(resultnames, 0);
+        WorkSource ws1 = wsNew(lhs, lhsnames);
+        WorkSource ws2 = wsNew(rhs, rhsnames);
+        boolean diffres = ws1.remove(ws2);
+        if (diffres != diff) {
+            StringBuilder sb = new StringBuilder();
+            sb.append("Expected diff ");
+            sb.append(diff);
+            sb.append(" but got ");
+            sb.append(diffres);
+            sb.append(" when removing ");
+            sb.append(ws2);
+            sb.append(" from ");
+            sb.append(ws1);
+            fail(sb.toString());
+        }
+        checkWorkSource("Remove", ws1, result, resultnames);
+    }
+
+    private void doTestRemoveUidsNames(int[] lhs, String[] lhsnames, int[] rhs, String[] rhsnames,
+            int[] result, String[] resultnames, boolean diff) throws Exception {
+        WorkSource ws1 = wsNew(lhs, lhsnames);
+        WorkSource ws2 = wsNew(rhs, rhsnames);
+        boolean diffres = ws1.remove(ws2);
+        if (diffres != diff) {
+            StringBuilder sb = new StringBuilder();
+            sb.append("Expected diff ");
+            sb.append(diff);
+            sb.append(" but got ");
+            sb.append(diffres);
+            sb.append(" when removing ");
+            sb.append(ws2);
+            sb.append(" from ");
+            sb.append(ws1);
+            fail(sb.toString());
+        }
+        checkWorkSource("Remove", ws1, result, resultnames);
+    }
+
+    private void doTestRemove(int[] lhs, int[] rhs, int[] result, boolean diff) throws Exception {
+        doTestRemoveUids(lhs, rhs, result, diff);
+        doTestRemoveUidsNames(lhs, makeRepeatingStringArray(lhs, "A"),
+                rhs, makeRepeatingStringArray(rhs, "A"),
+                result, makeRepeatingStringArray(result, "A"),
+                diff);
+        doTestRemoveNames(makeStringArray(lhs), makeStringArray(rhs),
+                makeStringArray(result), diff);
+    }
+
+    public void testRemoveNone() throws Exception {
+        doTestRemove(
+                new int[] { 10, 20, 30, 40 },
+                new int[] { 1, 2, 35, 50 },
+                new int[] { 10, 20, 30, 40 },
+                false);
+    }
+
+    public void testRemoveMultiFront() throws Exception {
+        doTestRemove(
+                new int[] { 10, 20, 30, 40 },
+                new int[] { 1, 2, 10, 30 },
+                new int[] { 20, 40 },
+                true);
+    }
+
+    public void testRemoveMultiMiddle() throws Exception {
+        doTestRemove(
+                new int[] { 10, 20, 30, 40 },
+                new int[] { 20, 30 },
+                new int[] { 10, 40 },
+                true);
+    }
+
+    public void testRemoveMultiEnd() throws Exception {
+        doTestRemove(
+                new int[] { 10, 20, 30, 40 },
+                new int[] { 30, 40, 50 },
+                new int[] { 10, 20 },
+                true);
+    }
+
+    public void testRemoveMultiFull() throws Exception {
+        doTestRemove(
+                new int[] { 10, 20, 30, 40 },
+                new int[] { 1, 2, 20, 25, 35, 40 },
+                new int[] { 10, 30 },
+                true);
+    }
+
+    public void testRemoveMultiAll() throws Exception {
+        doTestRemove(
+                new int[] { 10, 20, 30, 40 },
+                new int[] { 10, 20, 30, 40 },
+                new int[] { },
+                true);
+    }
+
+    private void doTestStripNames(int[] uids, String[] names, int[] expected) throws Exception {
+        WorkSource ws1 = wsNew(uids, names);
+        WorkSource res = wsStripNames(ws1);
+        checkWorkSource("StripNames", res, expected);
+    }
+
+    public void testStripNamesSimple() throws Exception {
+        doTestStripNames(
+                new int[]    { 10,  20,  30,  40 },
+                new String[] { "A", "A", "A", "A" },
+                new int[]    { 10, 20, 30, 40 });
+    }
+
+    public void testStripNamesFull() throws Exception {
+        doTestStripNames(
+                new int[]    { 10,  10,  10,  10 },
+                new String[] { "A", "B", "C", "D" },
+                new int[]    { 10 });
+    }
+
+    public void testStripNamesComplex() throws Exception {
+        doTestStripNames(
+                new int[]    { 10,  20,  20,  30,  40,  40 },
+                new String[] { "A", "A", "B", "A", "A", "B" },
+                new int[]    { 10, 20, 30, 40 });
+    }
+}
diff --git a/tests/tests/permission2/src/android/permission2/cts/NoReceiveGsmSmsPermissionTest.java b/tests/tests/permission2/src/android/permission2/cts/NoReceiveGsmSmsPermissionTest.java
deleted file mode 100644
index 9b20d1d..0000000
--- a/tests/tests/permission2/src/android/permission2/cts/NoReceiveGsmSmsPermissionTest.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.permission2.cts;
-
-import android.app.PendingIntent;
-import android.telephony.gsm.SmsManager;
-
-/**
- * Verify Sms and Mms cannot be received without required permissions.
- * Uses {@link android.telephony.gsm.SmsManager}.
- */
-@SuppressWarnings("deprecation")
-public class NoReceiveGsmSmsPermissionTest extends NoReceiveSmsPermissionTest {
-
-    protected void sendSms(PendingIntent sentIntent, PendingIntent deliveryIntent,
-            String currentNumber) {
-        SmsManager.getDefault().sendTextMessage(currentNumber, null, "test message",
-                sentIntent, deliveryIntent);
-    }
-}
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_AlbumsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_AlbumsTest.java
index 100d9e1..b265cbf 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_AlbumsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_AlbumsTest.java
@@ -45,12 +45,15 @@
     }
 
     public void testGetContentUri() {
-        assertNotNull(mContentResolver.query(
+        Cursor c = null;
+        assertNotNull(c = mContentResolver.query(
                 Albums.getContentUri(MediaStoreAudioTestHelper.INTERNAL_VOLUME_NAME), null, null,
                 null, null));
-        assertNotNull(mContentResolver.query(
+        c.close();
+        assertNotNull(c = mContentResolver.query(
                 Albums.getContentUri(MediaStoreAudioTestHelper.EXTERNAL_VOLUME_NAME), null, null,
                 null, null));
+        c.close();
 
         // can not accept any other volume names
         String volume = "fakeVolume";
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_ArtistsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_ArtistsTest.java
index 2e3cc1b..9fe31c5 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_ArtistsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_ArtistsTest.java
@@ -37,12 +37,15 @@
     }
 
     public void testGetContentUri() {
-        assertNotNull(mContentResolver.query(
+        Cursor c = null;
+        assertNotNull(c = mContentResolver.query(
                 Artists.getContentUri(MediaStoreAudioTestHelper.INTERNAL_VOLUME_NAME), null, null,
                     null, null));
-        assertNotNull(mContentResolver.query(
+        c.close();
+        assertNotNull(c = mContentResolver.query(
                 Artists.getContentUri(MediaStoreAudioTestHelper.INTERNAL_VOLUME_NAME), null, null,
                 null, null));
+        c.close();
 
         // can not accept any other volume names
         String volume = "fakeVolume";
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Artists_AlbumsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Artists_AlbumsTest.java
index fdba619..72d9067 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Artists_AlbumsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Artists_AlbumsTest.java
@@ -39,12 +39,16 @@
     }
 
     public void testGetContentUri() {
+        Cursor c = null;
         Uri contentUri = MediaStore.Audio.Artists.Albums.getContentUri(
                 MediaStoreAudioTestHelper.INTERNAL_VOLUME_NAME, 1);
-        assertNotNull(mContentResolver.query(contentUri, null, null, null, null));
+        assertNotNull(c = mContentResolver.query(contentUri, null, null, null, null));
+        c.close();
+
         contentUri = MediaStore.Audio.Artists.Albums.getContentUri(
                 MediaStoreAudioTestHelper.EXTERNAL_VOLUME_NAME, 1);
-        assertNotNull(mContentResolver.query(contentUri, null, null, null, null));
+        assertNotNull(c = mContentResolver.query(contentUri, null, null, null, null));
+        c.close();
 
         // can not accept any other volume names
         String volume = "fakeVolume";
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_GenresTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_GenresTest.java
index 1138233..8d41b38 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_GenresTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_GenresTest.java
@@ -39,14 +39,16 @@
     }
 
     public void testGetContentUri() {
-        assertNotNull(mContentResolver.query(
+        Cursor c = null;
+        assertNotNull(c = mContentResolver.query(
                 Genres.getContentUri(MediaStoreAudioTestHelper.EXTERNAL_VOLUME_NAME), null, null,
                     null, null));
-
+        c.close();
         try {
-            assertNotNull(mContentResolver.query(
+            assertNotNull(c = mContentResolver.query(
                     Genres.getContentUri(MediaStoreAudioTestHelper.INTERNAL_VOLUME_NAME), null,
                         null, null, null));
+            c.close();
             fail("Should throw SQLException as the internal datatbase has no genre");
         } catch (SQLException e) {
             // expected
@@ -98,6 +100,7 @@
         // Insert an audio file into the content provider.
         ContentValues values = Audio1.getInstance().getContentValues(true);
         Uri audioUri = mContentResolver.insert(Media.EXTERNAL_CONTENT_URI, values);
+        assertNotNull(audioUri);
         long audioId = ContentUris.parseId(audioUri);
         assertTrue(audioId != -1);
 
@@ -105,6 +108,7 @@
         values.clear();
         values.put(Genres.NAME, "Soda Pop");
         Uri genreUri = mContentResolver.insert(Genres.EXTERNAL_CONTENT_URI, values);
+        assertNotNull(genreUri);
         long genreId = ContentUris.parseId(genreUri);
         assertTrue(genreId != -1);
 
@@ -131,6 +135,8 @@
             if (cursor != null) {
                 cursor.close();
             }
+            assertEquals(1, mContentResolver.delete(audioUri, null, null));
+            assertEquals(1, mContentResolver.delete(genreUri, null, null));
         }
     }
 }
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Genres_MembersTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Genres_MembersTest.java
index 2e389b6..7310fa1 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Genres_MembersTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Genres_MembersTest.java
@@ -65,14 +65,17 @@
     }
 
     public void testGetContentUri() {
-        assertNotNull(mContentResolver.query(
+        Cursor c = null;
+        assertNotNull(c = mContentResolver.query(
                 Members.getContentUri(MediaStoreAudioTestHelper.EXTERNAL_VOLUME_NAME, 1), null,
                     null, null, null));
+        c.close();
 
         try {
-            assertNotNull(mContentResolver.query(
+            assertNotNull(c = mContentResolver.query(
                     Members.getContentUri(MediaStoreAudioTestHelper.INTERNAL_VOLUME_NAME, 1), null,
                         null, null, null));
+            c.close();
             fail("Should throw SQLException as the internal datatbase has no genre");
         } catch (SQLException e) {
             // expected
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_MediaTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_MediaTest.java
index 8a4e3ee..82c2342 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_MediaTest.java
@@ -38,12 +38,15 @@
     }
 
     public void testGetContentUri() {
-        assertNotNull(mContentResolver.query(
+        Cursor c = null;
+        assertNotNull(c = mContentResolver.query(
                 Media.getContentUri(MediaStoreAudioTestHelper.INTERNAL_VOLUME_NAME), null, null,
                     null, null));
-        assertNotNull(mContentResolver.query(
+        c.close();
+        assertNotNull(c = mContentResolver.query(
                 Media.getContentUri(MediaStoreAudioTestHelper.EXTERNAL_VOLUME_NAME), null, null,
                     null, null));
+        c.close();
 
         // can not accept any other volume names
         String volume = "faveVolume";
@@ -51,14 +54,17 @@
     }
 
     public void testGetContentUriForPath() {
+        Cursor c = null;
         String externalPath = Environment.getExternalStorageDirectory().getPath();
-        assertNotNull(mContentResolver.query(Media.getContentUriForPath(externalPath), null, null,
+        assertNotNull(c = mContentResolver.query(Media.getContentUriForPath(externalPath), null, null,
                 null, null));
+        c.close();
 
         String internalPath =
             getInstrumentation().getTargetContext().getFilesDir().getAbsolutePath();
-        assertNotNull(mContentResolver.query(Media.getContentUriForPath(internalPath), null, null,
+        assertNotNull(c = mContentResolver.query(Media.getContentUriForPath(internalPath), null, null,
                 null, null));
+        c.close();
     }
 
     public void testStoreAudioMediaInternal() {
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_PlaylistsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_PlaylistsTest.java
index 0d36212..03a81b8 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_PlaylistsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_PlaylistsTest.java
@@ -39,15 +39,18 @@
     }
 
     public void testGetContentUri() {
-        assertNotNull(mContentResolver.query(
+        Cursor c = null;
+        assertNotNull(c = mContentResolver.query(
                 Playlists.getContentUri(MediaStoreAudioTestHelper.EXTERNAL_VOLUME_NAME), null, null,
                 null, null));
+        c.close();
 
         // can not accept any other volume names
         try {
-            assertNotNull(mContentResolver.query(
+            assertNotNull(c = mContentResolver.query(
                     Playlists.getContentUri(MediaStoreAudioTestHelper.INTERNAL_VOLUME_NAME), null,
                     null, null, null));
+            c.close();
             fail("Should throw SQLException as the internal datatbase has no playlist");
         } catch (SQLException e) {
             // expected
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Playlists_MembersTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Playlists_MembersTest.java
index aa524aa..bf97848 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Playlists_MembersTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Playlists_MembersTest.java
@@ -27,6 +27,9 @@
 import android.provider.MediaStore.Audio.Playlists.Members;
 import android.provider.cts.MediaStoreAudioTestHelper.Audio1;
 import android.provider.cts.MediaStoreAudioTestHelper.Audio2;
+import android.provider.cts.MediaStoreAudioTestHelper.Audio3;
+import android.provider.cts.MediaStoreAudioTestHelper.Audio4;
+import android.provider.cts.MediaStoreAudioTestHelper.Audio5;
 import android.provider.cts.MediaStoreAudioTestHelper.MockAudioMediaInfo;
 import android.test.InstrumentationTestCase;
 
@@ -114,9 +117,9 @@
         mContentResolver = getInstrumentation().getContext().getContentResolver();
         mIdOfAudio1 = insertAudioItem(Audio1.getInstance());
         mIdOfAudio2 = insertAudioItem(Audio2.getInstance());
-        mIdOfAudio3 = insertAudioItem(Audio1.getInstance());
-        mIdOfAudio4 = insertAudioItem(Audio1.getInstance());
-        mIdOfAudio5 = insertAudioItem(Audio1.getInstance());
+        mIdOfAudio3 = insertAudioItem(Audio3.getInstance());
+        mIdOfAudio4 = insertAudioItem(Audio4.getInstance());
+        mIdOfAudio5 = insertAudioItem(Audio5.getInstance());
     }
 
     @Override
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java
index 1e8a4ac..79b2e57 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java
@@ -84,6 +84,10 @@
         assertEquals(1, mResolver.update(fileUri, values, null, null));
         assertStringColumn(fileUri, MediaColumns.DATA, updatedPath);
 
+        // check that inserting a duplicate entry fails
+        Uri foo = mResolver.insert(allFilesUri, values);
+        assertNull(foo);
+
         // Delete the file and observe that the file count decreased.
         assertEquals(1, mResolver.delete(fileUri, null, null));
         assertEquals(fileCount, getFileCount(allFilesUri));
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
index fa269e4..740e31b 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
@@ -178,10 +178,13 @@
     }
 
     public void testGetContentUri() {
-        assertNotNull(mContentResolver.query(Media.getContentUri("internal"), null, null, null,
+        Cursor c = null;
+        assertNotNull(c = mContentResolver.query(Media.getContentUri("internal"), null, null, null,
                 null));
-        assertNotNull(mContentResolver.query(Media.getContentUri("external"), null, null, null,
+        c.close();
+        assertNotNull(c = mContentResolver.query(Media.getContentUri("external"), null, null, null,
                 null));
+        c.close();
 
         // can not accept any other volume names
         String volume = "fakeVolume";
@@ -294,6 +297,7 @@
         } finally {
             // delete
             assertEquals(1, mContentResolver.delete(uri, null, null));
+            new File(externalPath).delete();
         }
     }
 
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java
index 8ecea7b..e8a13a9 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java
@@ -211,10 +211,13 @@
     }
 
     public void testGetContentUri() {
-        assertNotNull(mContentResolver.query(Thumbnails.getContentUri("internal"), null, null,
+        Cursor c = null;
+        assertNotNull(c = mContentResolver.query(Thumbnails.getContentUri("internal"), null, null,
                 null, null));
-        assertNotNull(mContentResolver.query(Thumbnails.getContentUri("external"), null, null,
+        c.close();
+        assertNotNull(c = mContentResolver.query(Thumbnails.getContentUri("external"), null, null,
                 null, null));
+        c.close();
 
         // can not accept any other volume names
         String volume = "fakeVolume";
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java
index f4d01e3..c3eb0b8 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java
@@ -45,10 +45,13 @@
     }
 
     public void testGetContentUri() {
-        assertNotNull(mContentResolver.query(Media.getContentUri("internal"), null, null, null,
+        Cursor c = null;
+        assertNotNull(c = mContentResolver.query(Media.getContentUri("internal"), null, null, null,
                 null));
-        assertNotNull(mContentResolver.query(Media.getContentUri("external"), null, null, null,
+        c.close();
+        assertNotNull(c = mContentResolver.query(Media.getContentUri("external"), null, null, null,
                 null));
+        c.close();
 
         // can not accept any other volume names
         String volume = "fakeVolume";
@@ -180,6 +183,7 @@
         } finally {
             // delete
             assertEquals(1, mContentResolver.delete(uri, null, null));
+            new File(externalVideoPath).delete();
         }
 
         // check that the video file is removed when deleting the database entry
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java
index 2a4b46c..8f6f729 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java
@@ -99,6 +99,8 @@
             assertFalse("thumbnail file should no longer exist", new File(path).exists());
         }
         c.close();
+
+        assertEquals(1, mResolver.delete(videoUri, null, null));
     }
 
     private Uri insertVideo() throws IOException {
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/AllocationTest.java b/tests/tests/renderscript/src/android/renderscript/cts/AllocationTest.java
index feac159..4a1533c 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/AllocationTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/AllocationTest.java
@@ -28,6 +28,8 @@
 import android.renderscript.Type.Builder;
 import android.renderscript.Type.CubemapFace;
 
+import android.util.Log;
+
 public class AllocationTest extends RSBaseGraphics {
 
     // Test power of two and non power of two, equal and non-equal sizes
@@ -188,10 +190,10 @@
 
     void helperCreateFromBitmap(Bitmap B,
                                 Allocation.MipmapControl mc) {
-        for (int i = 0; i < 1; i++) {
-            for (int j = 0; j < 1; j++) {
-                for (int k = 0; k < 1; k++) {
-                    for (int l = 0; l < 1; l++) {
+        for (int i = 0; i <= 1; i++) {
+            for (int j = 0; j <= 1; j++) {
+                for (int k = 0; k <= 1; k++) {
+                    for (int l = 0; l <= 1; l++) {
                         int u = 0;
                         u |= (i * Allocation.USAGE_SCRIPT);
                         u |= (j * Allocation.USAGE_GRAPHICS_TEXTURE);
@@ -684,6 +686,101 @@
             }
         }
     }
+
+    public void testCopyFromAllocation() {
+        int nElemsX = 256;
+        int nElemsY = 32;
+        Type.Builder b = new Type.Builder(mRS, Element.I8(mRS));
+        Type.Builder b2 = new Type.Builder(mRS, Element.I32(mRS));
+        Allocation srcA = Allocation.createTyped(mRS, b.setX(nElemsX).setY(nElemsY).create());
+        Allocation dstA = Allocation.createTyped(mRS, b.setX(nElemsX).setY(nElemsY).create());
+
+        // wrong dimensionality
+        Allocation srcB_bad = Allocation.createTyped(mRS, b.setX(nElemsX).setY(1).create());
+        Allocation srcC_bad = Allocation.createTyped(mRS, b.setX(nElemsY).setY(nElemsX).create());
+        Allocation srcD_bad = Allocation.createTyped(mRS, b.setX(nElemsX*2).setY(nElemsY*2).create());
+
+        // wrong element type
+        Allocation srcE_bad = Allocation.createTyped(mRS, b2.setX(nElemsX).setY(nElemsY).create());
+
+        try {
+            dstA.copyFrom(srcB_bad);
+            fail("should throw RSIllegalArgumentException");
+        } catch (RSIllegalArgumentException e) {
+        }
+
+        try {
+            dstA.copyFrom(srcC_bad);
+            fail("should throw RSIllegalArgumentException");
+        } catch (RSIllegalArgumentException e) {
+        }
+
+        try {
+            dstA.copyFrom(srcD_bad);
+            fail("should throw RSIllegalArgumentException");
+        } catch (RSIllegalArgumentException e) {
+        }
+
+        try {
+            dstA.copyFrom(srcE_bad);
+            fail("should throw RSIllegalArgumentException");
+        } catch (RSIllegalArgumentException e) {
+        }
+
+        dstA.copyFrom(srcA);
+
+    }
+
+    public void testSetElementAt() {
+        Type.Builder b = new Type.Builder(mRS, Element.I32(mRS));
+        Allocation largeArray = Allocation.createTyped(mRS, b.setX(48).create());
+        Allocation singleElement = Allocation.createTyped(mRS, b.setX(1).create());
+
+        ScriptC_setelementat script = new ScriptC_setelementat(mRS, mRes, R.raw.setelementat);
+
+        script.set_memset_toValue(1);
+        script.forEach_memset(singleElement);
+
+        script.set_dimX(48);
+        script.set_array(largeArray);
+
+        script.forEach_setLargeArray(singleElement);
+
+        int[] result = new int[1];
+
+        script.set_compare_value(10);
+        script.forEach_compare(largeArray);
+        script.forEach_getCompareResult(singleElement);
+        singleElement.copyTo(result);
+        assertTrue(result[0] == 2);
+    }
+
+    public void testSetElementAt2D() {
+        Type.Builder b = new Type.Builder(mRS, Element.I32(mRS));
+
+        Allocation singleElement = Allocation.createTyped(mRS, b.setX(1).create());
+        Allocation largeArray = Allocation.createTyped(mRS, b.setX(48).setY(16).create());
+
+        ScriptC_setelementat script = new ScriptC_setelementat(mRS, mRes, R.raw.setelementat);
+
+        script.set_memset_toValue(1);
+        script.forEach_memset(singleElement);
+
+        script.set_dimX(48);
+        script.set_dimY(16);
+        script.set_array(largeArray);
+
+        script.forEach_setLargeArray2D(singleElement);
+
+        int[] result = new int[1];
+
+        script.set_compare_value(10);
+        script.forEach_compare(largeArray);
+        script.forEach_getCompareResult(singleElement);
+        singleElement.copyTo(result);
+        assertTrue(result[0] == 2);
+    }
+
 }
 
 
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ElementTest.java b/tests/tests/renderscript/src/android/renderscript/cts/ElementTest.java
index 2ad45b1..ead4528 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ElementTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ElementTest.java
@@ -487,11 +487,12 @@
         assertEquals(DataKind.PIXEL_RGB, DataKind.valueOf("PIXEL_RGB"));
         assertEquals(DataKind.PIXEL_RGBA, DataKind.valueOf("PIXEL_RGBA"));
         assertEquals(DataKind.PIXEL_DEPTH, DataKind.valueOf("PIXEL_DEPTH"));
+        assertEquals(DataKind.PIXEL_YUV, DataKind.valueOf("PIXEL_YUV"));
         // Make sure no new enums are added
-        assertEquals(7, DataKind.values().length);
+        assertEquals(8, DataKind.values().length);
 
         for (DataKind dk : DataKind.values()) {
-            if (dk != DataKind.USER) {
+            if (dk != DataKind.USER && dk != DataKind.PIXEL_YUV) {
                 Element.createPixel(mRS, DataType.UNSIGNED_8, dk);
             }
         }
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ImageProcessingTest.java b/tests/tests/renderscript/src/android/renderscript/cts/ImageProcessingTest.java
new file mode 100644
index 0000000..fae20f4
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ImageProcessingTest.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+
+import android.renderscript.Byte2;
+import android.renderscript.Byte3;
+import android.renderscript.Byte4;
+
+import android.renderscript.Double2;
+import android.renderscript.Double3;
+import android.renderscript.Double4;
+
+import android.renderscript.Element;
+
+import android.renderscript.Float2;
+import android.renderscript.Float3;
+import android.renderscript.Float4;
+
+import android.renderscript.Int2;
+import android.renderscript.Int3;
+import android.renderscript.Int4;
+
+import android.renderscript.Long2;
+import android.renderscript.Long3;
+import android.renderscript.Long4;
+
+import android.renderscript.RSRuntimeException;
+
+import android.renderscript.Short2;
+import android.renderscript.Short3;
+import android.renderscript.Short4;
+
+import android.renderscript.Matrix4f;
+
+import android.renderscript.Type;
+
+import android.renderscript.ScriptGroup;
+
+import android.renderscript.ScriptIntrinsicBlend;
+import android.renderscript.ScriptIntrinsicBlur;
+import android.renderscript.ScriptIntrinsicColorMatrix;
+import android.renderscript.ScriptIntrinsicConvolve3x3;
+import android.renderscript.ScriptIntrinsicConvolve5x5;
+import android.renderscript.ScriptIntrinsicLUT;
+
+import com.android.cts.stub.R;
+
+public class ImageProcessingTest extends RSBaseCompute {
+    private Allocation a1, a2;
+
+    private final int MAX_RADIUS = 25;
+    private final int dimX = 256;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        Type t = new Type.Builder(mRS, Element.U8_4(mRS)).setX(dimX).setY(dimX).create();
+        a1 = Allocation.createTyped(mRS, t);
+        a2 = Allocation.createTyped(mRS, t);
+    }
+
+    public void testBlur() {
+        ScriptIntrinsicBlur mBlur;
+        mBlur = ScriptIntrinsicBlur.create(mRS, Element.U8_4(mRS));
+
+        Allocation a1_copy, a2_copy;
+        a1_copy = Allocation.createTyped(mRS, a1.getType());
+        a2_copy = Allocation.createTyped(mRS, a2.getType());
+
+        for (int i = 1; i < MAX_RADIUS; i++) {
+
+            a1_copy.copy2DRangeFrom(0, 0, a1.getType().getX(), a1.getType().getY(), a1, 0, 0);
+
+            mBlur.setRadius(i);
+            mBlur.setInput(a1_copy);
+
+            mBlur.forEach(a2_copy);
+
+            // validate
+
+        }
+
+    }
+
+    public void testBlend() {
+        ScriptIntrinsicBlend mBlend;
+        mBlend = ScriptIntrinsicBlend.create(mRS, Element.U8_4(mRS));
+
+        Allocation a1_copy, a2_copy;
+        a1_copy = Allocation.createTyped(mRS, a1.getType());
+        a2_copy = Allocation.createTyped(mRS, a2.getType());
+
+        for (int i = 0; i < 14; i++) {
+
+            a1_copy.copy2DRangeFrom(0, 0, a1.getType().getX(), a1.getType().getY(), a1, 0, 0);
+            a2_copy.copy2DRangeFrom(0, 0, a2.getType().getX(), a2.getType().getY(), a2, 0, 0);
+
+            switch (i) {
+            case 0:
+                mBlend.forEachSrc(a1_copy, a2_copy);
+                break;
+            case 1:
+                mBlend.forEachDst(a1_copy, a2_copy);
+                break;
+            case 2:
+                mBlend.forEachSrcOver(a1_copy, a2_copy);
+                break;
+            case 3:
+                mBlend.forEachDstOver(a1_copy, a2_copy);
+                break;
+            case 4:
+                mBlend.forEachSrcIn(a1_copy, a2_copy);
+                break;
+            case 5:
+                mBlend.forEachDstIn(a1_copy, a2_copy);
+                break;
+            case 6:
+                mBlend.forEachSrcOut(a1_copy, a2_copy);
+                break;
+            case 7:
+                mBlend.forEachDstOut(a1_copy, a2_copy);
+                break;
+            case 8:
+                mBlend.forEachSrcAtop(a1_copy, a2_copy);
+                break;
+            case 9:
+                mBlend.forEachDstAtop(a1_copy, a2_copy);
+                break;
+            case 10:
+                mBlend.forEachXor(a1_copy, a2_copy);
+                break;
+            case 11:
+                mBlend.forEachAdd(a1_copy, a2_copy);
+                break;
+            case 12:
+                mBlend.forEachSubtract(a1_copy, a2_copy);
+                break;
+            case 13:
+                mBlend.forEachMultiply(a1_copy, a2_copy);
+                break;
+            }
+
+            // validate
+
+        }
+
+    }
+
+    public void testColorMatrix() {
+        ScriptIntrinsicColorMatrix mColorMatrix;
+        mColorMatrix = ScriptIntrinsicColorMatrix.create(mRS, Element.U8_4(mRS));
+
+        Allocation a1_copy, a2_copy;
+        a1_copy = Allocation.createTyped(mRS, a1.getType());
+        a2_copy = Allocation.createTyped(mRS, a2.getType());
+
+        Matrix4f m = new Matrix4f();
+        m.set(1, 0, 0.2f);
+        m.set(1, 1, 0.9f);
+        m.set(1, 2, 0.2f);
+
+        //test greyscale
+        mColorMatrix.setGreyscale();
+
+        a1_copy.copy2DRangeFrom(0, 0, a1.getType().getX(), a1.getType().getY(), a1, 0, 0);
+        a2_copy.copy2DRangeFrom(0, 0, a2.getType().getX(), a2.getType().getY(), a2, 0, 0);
+
+        mColorMatrix.forEach(a1_copy, a2_copy);
+
+        //validate greyscale
+
+
+        //test color matrix
+        mColorMatrix.setColorMatrix(m);
+
+        a1_copy.copy2DRangeFrom(0, 0, a1.getType().getX(), a1.getType().getY(), a1, 0, 0);
+        a2_copy.copy2DRangeFrom(0, 0, a2.getType().getX(), a2.getType().getY(), a2, 0, 0);
+
+        mColorMatrix.forEach(a1_copy, a2_copy);
+
+        //validate color matrix
+
+
+    }
+
+
+    public void testConvolve3x3() {
+        ScriptIntrinsicConvolve3x3 mConvolve3x3;
+        mConvolve3x3 = ScriptIntrinsicConvolve3x3.create(mRS, Element.U8_4(mRS));
+
+        Allocation a1_copy, a2_copy;
+        a1_copy = Allocation.createTyped(mRS, a1.getType());
+        a2_copy = Allocation.createTyped(mRS, a2.getType());
+
+        a1_copy.copy2DRangeFrom(0, 0, a1.getType().getX(), a1.getType().getY(), a1, 0, 0);
+
+        float f[] = new float[9];
+        f[0] =  0.f;    f[1] = -1.f;    f[2] =  0.f;
+        f[3] = -1.f;    f[4] =  5.f;    f[5] = -1.f;
+        f[6] =  0.f;    f[7] = -1.f;    f[8] =  0.f;
+
+        mConvolve3x3.setCoefficients(f);
+        mConvolve3x3.setInput(a1_copy);
+        mConvolve3x3.forEach(a2_copy);
+
+        // validate
+
+    }
+
+    public void testConvolve5x5() {
+        ScriptIntrinsicConvolve5x5 mConvolve5x5;
+        mConvolve5x5 = ScriptIntrinsicConvolve5x5.create(mRS, Element.U8_4(mRS));
+
+        Allocation a1_copy, a2_copy;
+        a1_copy = Allocation.createTyped(mRS, a1.getType());
+        a2_copy = Allocation.createTyped(mRS, a2.getType());
+
+        a1_copy.copy2DRangeFrom(0, 0, a1.getType().getX(), a1.getType().getY(), a1, 0, 0);
+
+        float f[] = new float[25];
+        f[0] = -1.f; f[1] = -3.f; f[2] = -4.f; f[3] = -3.f; f[4] = -1.f;
+        f[5] = -3.f; f[6] =  0.f; f[7] =  6.f; f[8] =  0.f; f[9] = -3.f;
+        f[10]= -4.f; f[11]=  6.f; f[12]= 20.f; f[13]=  6.f; f[14]= -4.f;
+        f[15]= -3.f; f[16]=  0.f; f[17]=  6.f; f[18]=  0.f; f[19]= -3.f;
+        f[20]= -1.f; f[21]= -3.f; f[22]= -4.f; f[23]= -3.f; f[24]= -1.f;
+
+        mConvolve5x5.setCoefficients(f);
+        mConvolve5x5.setInput(a1_copy);
+        mConvolve5x5.forEach(a2_copy);
+
+        // validate
+
+    }
+
+    public void testLUT() {
+        ScriptIntrinsicLUT mLUT;
+        mLUT = ScriptIntrinsicLUT.create(mRS, Element.U8_4(mRS));
+
+        Allocation a1_copy, a2_copy;
+        a1_copy = Allocation.createTyped(mRS, a1.getType());
+        a2_copy = Allocation.createTyped(mRS, a2.getType());
+
+        a1_copy.copy2DRangeFrom(0, 0, a1.getType().getX(), a1.getType().getY(), a1, 0, 0);
+
+        for (int ct=0; ct < 256; ct++) {
+            float f = ((float)ct) / 255.f;
+
+            float r = f;
+            if (r < 0.5f) {
+                r = 4.0f * r * r * r;
+            } else {
+                r = 1.0f - r;
+                r = 1.0f - (4.0f * r * r * r);
+            }
+            mLUT.setRed(ct, (int)(r * 255.f + 0.5f));
+
+            float g = f;
+            if (g < 0.5f) {
+                g = 2.0f * g * g;
+            } else {
+                g = 1.0f - g;
+                g = 1.0f - (2.0f * g * g);
+            }
+            mLUT.setGreen(ct, (int)(g * 255.f + 0.5f));
+
+            float b = f * 0.5f + 0.25f;
+            mLUT.setBlue(ct, (int)(b * 255.f + 0.5f));
+        }
+
+        mLUT.forEach(a1_copy, a2_copy);
+
+        // validate
+
+    }
+
+    public void testScriptGroup() {
+        ScriptGroup group;
+
+        ScriptIntrinsicConvolve3x3 mConvolve3x3;
+        ScriptIntrinsicColorMatrix mColorMatrix;
+
+        mConvolve3x3 = ScriptIntrinsicConvolve3x3.create(mRS, Element.U8_4(mRS));
+        mColorMatrix = ScriptIntrinsicColorMatrix.create(mRS, Element.U8_4(mRS));
+
+        Allocation a1_copy, a2_copy;
+        a1_copy = Allocation.createTyped(mRS, a1.getType());
+        a2_copy = Allocation.createTyped(mRS, a2.getType());
+
+        a1_copy.copy2DRangeFrom(0, 0, a1.getType().getX(), a1.getType().getY(), a1, 0, 0);
+
+        float f[] = new float[9];
+        f[0] =  0.f;    f[1] = -1.f;    f[2] =  0.f;
+        f[3] = -1.f;    f[4] =  5.f;    f[5] = -1.f;
+        f[6] =  0.f;    f[7] = -1.f;    f[8] =  0.f;
+
+        mConvolve3x3.setCoefficients(f);
+
+        Matrix4f m = new Matrix4f();
+        m.set(1, 0, 0.2f);
+        m.set(1, 1, 0.9f);
+        m.set(1, 2, 0.2f);
+        mColorMatrix.setColorMatrix(m);
+
+        Type connect = new Type.Builder(mRS, Element.U8_4(mRS)).setX(dimX).setY(dimX).create();
+
+        ScriptGroup.Builder b = new ScriptGroup.Builder(mRS);
+        b.addKernel(mConvolve3x3.getKernelID());
+        b.addKernel(mColorMatrix.getKernelID());
+        b.addConnection(connect, mConvolve3x3.getKernelID(), mColorMatrix.getKernelID());
+        group = b.create();
+
+        mConvolve3x3.setInput(a1_copy);
+        group.setOutput(mColorMatrix.getKernelID(), a2_copy);
+        group.execute();
+
+        // validate
+
+    }
+
+
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_11.java b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_11.java
index 6ad1748..a16059a 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_11.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_11.java
@@ -27,7 +27,16 @@
  * @hide
  */
 public class ScriptC_set_target_api_11 extends ScriptC {
+    private static final String __rs_resource_name = "set_target_api_11";
     // Constructor
+    public  ScriptC_set_target_api_11(RenderScript rs) {
+        this(rs,
+             rs.getApplicationContext().getResources(),
+             rs.getApplicationContext().getResources().getIdentifier(
+                 __rs_resource_name, "raw",
+                 rs.getApplicationContext().getPackageName()));
+    }
+
     public  ScriptC_set_target_api_11(RenderScript rs, Resources resources, int id) {
         super(rs, resources, id);
     }
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_12.java b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_12.java
index dc772d5..5c31f94 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_12.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_12.java
@@ -27,7 +27,16 @@
  * @hide
  */
 public class ScriptC_set_target_api_12 extends ScriptC {
+    private static final String __rs_resource_name = "set_target_api_12";
     // Constructor
+    public  ScriptC_set_target_api_12(RenderScript rs) {
+        this(rs,
+             rs.getApplicationContext().getResources(),
+             rs.getApplicationContext().getResources().getIdentifier(
+                 __rs_resource_name, "raw",
+                 rs.getApplicationContext().getPackageName()));
+    }
+
     public  ScriptC_set_target_api_12(RenderScript rs, Resources resources, int id) {
         super(rs, resources, id);
     }
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_13.java b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_13.java
index e1580e4..e12bf00 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_13.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_13.java
@@ -27,7 +27,16 @@
  * @hide
  */
 public class ScriptC_set_target_api_13 extends ScriptC {
+    private static final String __rs_resource_name = "set_target_api_13";
     // Constructor
+    public  ScriptC_set_target_api_13(RenderScript rs) {
+        this(rs,
+             rs.getApplicationContext().getResources(),
+             rs.getApplicationContext().getResources().getIdentifier(
+                 __rs_resource_name, "raw",
+                 rs.getApplicationContext().getPackageName()));
+    }
+
     public  ScriptC_set_target_api_13(RenderScript rs, Resources resources, int id) {
         super(rs, resources, id);
     }
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_14.java b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_14.java
index 42d08c6..97879a5 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_14.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_14.java
@@ -27,7 +27,16 @@
  * @hide
  */
 public class ScriptC_set_target_api_14 extends ScriptC {
+    private static final String __rs_resource_name = "set_target_api_14";
     // Constructor
+    public  ScriptC_set_target_api_14(RenderScript rs) {
+        this(rs,
+             rs.getApplicationContext().getResources(),
+             rs.getApplicationContext().getResources().getIdentifier(
+                 __rs_resource_name, "raw",
+                 rs.getApplicationContext().getPackageName()));
+    }
+
     public  ScriptC_set_target_api_14(RenderScript rs, Resources resources, int id) {
         super(rs, resources, id);
     }
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_15.java b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_15.java
index 6acfede..636a128 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_15.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_15.java
@@ -27,7 +27,16 @@
  * @hide
  */
 public class ScriptC_set_target_api_15 extends ScriptC {
+    private static final String __rs_resource_name = "set_target_api_15";
     // Constructor
+    public  ScriptC_set_target_api_15(RenderScript rs) {
+        this(rs,
+             rs.getApplicationContext().getResources(),
+             rs.getApplicationContext().getResources().getIdentifier(
+                 __rs_resource_name, "raw",
+                 rs.getApplicationContext().getPackageName()));
+    }
+
     public  ScriptC_set_target_api_15(RenderScript rs, Resources resources, int id) {
         super(rs, resources, id);
     }
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_16.java b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_16.java
index f111005..435316f 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_16.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_16.java
@@ -27,7 +27,16 @@
  * @hide
  */
 public class ScriptC_set_target_api_16 extends ScriptC {
+    private static final String __rs_resource_name = "set_target_api_16";
     // Constructor
+    public  ScriptC_set_target_api_16(RenderScript rs) {
+        this(rs,
+             rs.getApplicationContext().getResources(),
+             rs.getApplicationContext().getResources().getIdentifier(
+                 __rs_resource_name, "raw",
+                 rs.getApplicationContext().getPackageName()));
+    }
+
     public  ScriptC_set_target_api_16(RenderScript rs, Resources resources, int id) {
         super(rs, resources, id);
     }
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_17.java b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_17.java
new file mode 100644
index 0000000..c706ca6
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_17.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2011-2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file is auto-generated. DO NOT MODIFY!
+ * The source Renderscript file: set_target_api_17.rs
+ */
+package android.renderscript.cts;
+
+import android.renderscript.*;
+import android.content.res.Resources;
+
+/**
+ * @hide
+ */
+public class ScriptC_set_target_api_17 extends ScriptC {
+    private static final String __rs_resource_name = "set_target_api_17";
+    // Constructor
+    public  ScriptC_set_target_api_17(RenderScript rs) {
+        this(rs,
+             rs.getApplicationContext().getResources(),
+             rs.getApplicationContext().getResources().getIdentifier(
+                 __rs_resource_name, "raw",
+                 rs.getApplicationContext().getPackageName()));
+    }
+
+    public  ScriptC_set_target_api_17(RenderScript rs, Resources resources, int id) {
+        super(rs, resources, id);
+    }
+
+    private final static int mExportFuncIdx_check = 0;
+    public void invoke_check(int version) {
+        FieldPacker check_fp = new FieldPacker(4);
+        check_fp.addI32(version);
+        invoke(mExportFuncIdx_check, check_fp);
+    }
+
+}
+
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_too_high.java b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_too_high.java
new file mode 100644
index 0000000..f54ab46
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_too_high.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2011-2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file is auto-generated. DO NOT MODIFY!
+ * The source Renderscript file: set_target_api_16.rs
+ */
+package android.renderscript.cts;
+
+import android.renderscript.*;
+import android.content.res.Resources;
+
+/**
+ * @hide
+ */
+public class ScriptC_set_target_api_too_high extends ScriptC {
+    private static final String __rs_resource_name = "set_target_api_too_high";
+    // Constructor
+    public  ScriptC_set_target_api_too_high(RenderScript rs) {
+        this(rs,
+             rs.getApplicationContext().getResources(),
+             rs.getApplicationContext().getResources().getIdentifier(
+                 __rs_resource_name, "raw",
+                 rs.getApplicationContext().getPackageName()));
+    }
+
+    public  ScriptC_set_target_api_too_high(RenderScript rs, Resources resources, int id) {
+        super(rs, resources, id);
+    }
+
+    private final static int mExportFuncIdx_check = 0;
+    public void invoke_check(int version) {
+        FieldPacker check_fp = new FieldPacker(4);
+        check_fp.addI32(version);
+        invoke(mExportFuncIdx_check, check_fp);
+    }
+
+}
+
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ScriptGroupTest.java b/tests/tests/renderscript/src/android/renderscript/cts/ScriptGroupTest.java
index e73195c..64496ef 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ScriptGroupTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ScriptGroupTest.java
@@ -262,7 +262,6 @@
         compare.forEach_compare(out);
         compare.forEach_getCompareResult(resultAlloc);
         resultAlloc.copyTo(result);
-        Log.e("ARGH", "result = " + result[0]);
         assertTrue(result[0] == 2);
     }
 
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/VersionTest.java b/tests/tests/renderscript/src/android/renderscript/cts/VersionTest.java
index 6c9e7e9..c3966a9 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/VersionTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/VersionTest.java
@@ -17,6 +17,7 @@
 package android.renderscript.cts;
 
 import android.renderscript.RenderScript.RSMessageHandler;
+import android.renderscript.RSRuntimeException;
 import com.android.cts.stub.R;
 
 /**
@@ -82,4 +83,26 @@
         waitForMessage();
         checkForErrors();
     }
+
+    public void testVersion17() {
+        ScriptC_set_target_api_17 test17 =
+                new ScriptC_set_target_api_17(mRS,
+                                              mRes,
+                                              R.raw.set_target_api_17);
+        test17.invoke_check(17);
+        waitForMessage();
+        checkForErrors();
+    }
+
+    public void testVersion_too_high() {
+        try {
+            ScriptC_set_target_api_too_high test_too_high =
+                    new ScriptC_set_target_api_too_high(mRS,
+                                                        mRes,
+                                                        R.raw.set_target_api_too_high);
+            fail("should throw RSRuntimeException");
+        } catch (RSRuntimeException e) {
+        }
+        checkForErrors();
+    }
 }
diff --git a/tests/tests/security/src/android/security/cts/BannedFilesTest.java b/tests/tests/security/src/android/security/cts/BannedFilesTest.java
index ada9aaf..6ce8588 100644
--- a/tests/tests/security/src/android/security/cts/BannedFilesTest.java
+++ b/tests/tests/security/src/android/security/cts/BannedFilesTest.java
@@ -68,7 +68,7 @@
         if (!FileUtils.getFileStatus(file, fs, false)) {
             return;
         }
-        assertTrue((fs.mode & FileUtils.S_ISUID) == 0);
-        assertTrue((fs.mode & FileUtils.S_ISGID) == 0);
+        assertTrue("File \"" + file + "\" is setUID", (fs.mode & FileUtils.S_ISUID) == 0);
+        assertTrue("File \"" + file + "\" is setGID", (fs.mode & FileUtils.S_ISGID) == 0);
     }
 }
diff --git a/tests/tests/security/src/android/security/cts/CertificateData.java b/tests/tests/security/src/android/security/cts/CertificateData.java
index 1714461..1b039a5 100644
--- a/tests/tests/security/src/android/security/cts/CertificateData.java
+++ b/tests/tests/security/src/android/security/cts/CertificateData.java
@@ -40,6 +40,7 @@
       "40:54:DA:6F:1C:3F:40:74:AC:ED:0F:EC:CD:DB:79:D1:53:FB:90:1D",
       "43:F9:B1:10:D5:BA:FD:48:22:52:31:B0:D0:08:2B:37:2F:EF:9A:54",
       "F4:8B:11:BF:DE:AB:BE:94:54:20:71:E6:41:DE:6B:BE:88:2B:40:B9",
+      "58:E8:AB:B0:36:15:33:FB:80:F7:9B:1B:6D:29:D3:FF:8D:5F:00:F0",
       "96:56:CD:7B:57:96:98:95:D0:E1:41:46:68:06:FB:B8:C6:11:06:87",
       "55:A6:72:3E:CB:F2:EC:CD:C3:23:74:70:19:9D:2A:BE:11:E3:81:D1",
       "D6:9B:56:11:48:F0:1C:77:C5:45:78:C1:09:26:DF:5B:85:69:76:AD",
@@ -48,6 +49,7 @@
       "AD:7E:1C:28:B0:64:EF:8F:60:03:40:20:14:C3:D0:E3:37:0E:B5:8A",
       "8D:17:84:D5:37:F3:03:7D:EC:70:FE:57:8B:51:9A:99:E6:10:D7:B0",
       "AE:50:83:ED:7C:F4:5C:BC:8F:61:C6:21:FE:68:5D:79:42:21:15:6E",
+      "DA:FA:F7:FA:66:84:EC:06:8F:14:50:BD:C7:C2:81:A5:BC:A9:64:57",
       "5F:4E:1F:CF:31:B7:91:3B:85:0B:54:F6:E5:FF:50:1A:2B:6F:C6:CF",
       "74:F8:A3:C3:EF:E7:B3:90:06:4B:83:90:3C:21:64:60:20:E5:DF:CE",
       "85:B5:FF:67:9B:0C:79:96:1F:C8:6E:44:22:00:46:13:DB:17:92:84",
@@ -64,6 +66,7 @@
       "8C:F4:27:FD:79:0C:3A:D1:66:06:8D:E8:1E:57:EF:BB:93:22:72:D4",
       "56:E0:FA:C0:3B:8F:18:23:55:18:E5:D3:11:CA:E8:C2:43:31:AB:66",
       "02:72:68:29:3E:5F:5D:17:AA:A4:B3:C3:E6:36:1E:1F:92:57:5E:AA",
+      "2F:78:3D:25:52:18:A7:4A:65:39:71:B5:2C:A2:9C:45:15:6F:E9:19",
       "97:81:79:50:D8:1C:96:70:CC:34:D8:09:CF:79:44:31:36:7E:F4:74",
       "85:A4:08:C0:9C:19:3E:5D:51:58:7D:CD:D6:13:30:FD:8C:DE:37:BF",
       "58:11:9F:0E:12:82:87:EA:50:FD:D9:87:45:6F:4F:78:DC:FA:D6:D4",
@@ -98,6 +101,7 @@
       "69:BD:8C:F4:9C:D3:00:FB:59:2E:17:93:CA:55:6A:F3:EC:AA:35:FB",
       "13:2D:0D:45:53:4B:69:97:CD:B2:D5:C3:39:E2:55:76:60:9B:5C:C6",
       "5F:B7:EE:06:33:E2:59:DB:AD:0C:4C:9A:E6:D3:8F:1A:61:C7:DC:25",
+      "49:0A:75:74:DE:87:0A:47:FE:58:EE:F6:C7:6B:EB:C6:0B:12:40:99",
       "25:01:90:19:CF:FB:D9:99:1C:B7:68:25:74:8D:94:5F:30:93:95:42",
       "79:98:A3:08:E1:4D:65:85:E6:C2:1E:15:3A:71:9F:BA:5A:D3:4A:D9",
       "B5:1C:06:7C:EE:2B:0C:3D:F8:55:AB:2D:92:F4:FE:39:D4:E7:0F:0E",
@@ -142,6 +146,7 @@
       "21:FC:BD:8E:7F:6C:AF:05:1B:D1:B3:43:EC:A8:E7:61:47:F2:0F:8A",
       "5F:3B:8C:F2:F8:10:B3:7D:78:B4:CE:EC:19:19:C3:73:34:B9:C7:74",
       "2A:C8:D5:8B:57:CE:BF:2F:49:AF:F2:FC:76:8F:51:14:62:90:7A:41",
+      "96:C9:1B:0B:95:B4:10:98:42:FA:D0:D8:22:79:FE:60:FA:B9:16:83",
       "CA:BB:51:67:24:00:58:8E:64:19:F1:D4:08:78:D0:40:3A:A2:02:64",
       "5D:98:9C:DB:15:96:11:36:51:65:64:1B:56:0F:DB:EA:2A:C2:3E:F1",
       "D8:A6:33:2C:E0:03:6F:B1:85:F6:63:4F:7D:6A:06:65:26:32:28:27",
diff --git a/tests/tests/security/src/android/security/cts/KernelSettingsTest.java b/tests/tests/security/src/android/security/cts/KernelSettingsTest.java
index cc4b6a6..6daaffe 100644
--- a/tests/tests/security/src/android/security/cts/KernelSettingsTest.java
+++ b/tests/tests/security/src/android/security/cts/KernelSettingsTest.java
@@ -19,6 +19,7 @@
 import junit.framework.TestCase;
 
 import java.io.BufferedReader;
+import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileReader;
 import java.io.IOException;
@@ -86,6 +87,40 @@
         }
     }
 
+    /**
+     * Assert that support for loadable modules is not compiled into the
+     * kernel.
+     *
+     * Loadable modules are often used to implement rootkit like functionality.
+     * In addition, loadable modules enable support for /proc/sys/kernel/modprobe,
+     * which is commonly used by exploit writers to gain root access.
+     *
+     * Support for loadable modules can be removed by editing the Linux kernel
+     * config and removing the CONFIG_KMOD option.
+     */
+    public void testNoLoadableModules() throws IOException {
+        assertFalse(
+            "Support for loadable modules is compiled into the kernel. "
+                + "Loadable modules are often used by rootkits and other "
+                + "exploits and should be disabled. Please remove "
+                + "CONFIG_KMOD from your kernel config and compile "
+                + "all modules directly into the kernel.",
+            new File("/proc/sys/kernel/modprobe").exists());
+    }
+
+    /**
+     * Assert that the kernel config file is not compiled into the kernel.
+     *
+     * Compiling the config file into the kernel leaks the kernel base address
+     * via CONFIG_PHYS_OFFSET. It also wastes a small amount of valuable kernel memory.
+     */
+    public void testNoConfigGz() throws IOException {
+        assertFalse(
+                "/proc/config.gz is readable.  Please recompile your "
+                        + "kernel with CONFIG_IKCONFIG_PROC disabled",
+                new File("/proc/config.gz").exists());
+    }
+
     private String getFile(String filename) throws IOException {
         BufferedReader in = null;
         try {
diff --git a/tests/tests/text/src/android/text/method/cts/DigitsKeyListenerTest.java b/tests/tests/text/src/android/text/method/cts/DigitsKeyListenerTest.java
index 0a1b5ae..ea50294 100644
--- a/tests/tests/text/src/android/text/method/cts/DigitsKeyListenerTest.java
+++ b/tests/tests/text/src/android/text/method/cts/DigitsKeyListenerTest.java
@@ -64,8 +64,9 @@
      * 1. filter "123456", return null.
      * 2. filter "a1b2c3d", return "123"
      * 3. filter "-a1.b2c3d", return "123"
-     * 4. filter Spanned("-a1.b2c3d"), return Spanned("123") and copy spans.
-     * 5. filter "", return null
+     * 4. filter "+a1.b2c3d", return "123"
+     * 5. filter Spanned("-a1.b2c3d"), return Spanned("123") and copy spans.
+     * 6. filter "", return null
      */
     public void testFilter1() {
         String source = "123456";
@@ -87,6 +88,11 @@
                 dest, 0, dest.length())).toString());
         assertEquals(destString, dest.toString());
 
+        source = "+a1.b2c3d";
+        assertEquals("123", (digitsKeyListener.filter(source, 0, source.length(),
+                dest, 0, dest.length())).toString());
+        assertEquals(destString, dest.toString());
+
         Object what = new Object();
         Spannable spannableSource = new SpannableString(source);
         spannableSource.setSpan(what, 0, spannableSource.length(), Spanned.SPAN_POINT_POINT);
@@ -103,15 +109,21 @@
 
     /**
      * Check point:
-     * Current accepted characters are '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-'.
+     * Current accepted characters are '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '+'.
      * 1. filter "-123456", return null
-     * 2. filter "-a1.b2c3d", return "-123"
-     * 3. filter "-a1-b2c3d", return "-123"
-     * 4. filter "5-a1-b2c3d", return "5123"
-     * 5. filter Spanned("5-a1-b2c3d"), return Spanned("5123") and copy spans.
-     * 6. filter "", return null
-     * 7. filter "-123456" but dest has '-' after dend, return ""
-     * 8. filter "-123456" but dest has '-' before dstart, return "123456"
+     * 2. filter "+123456", return null
+     * 3. filter "-a1.b2c3d", return "-123"
+     * 4. filter "-a1-b2c3d", return "-123"
+     * 5. filter "+a1-b2c3d", return "+123"
+     * 6. filter "5-a1-b2c3d", return "5123"
+     * 7. filter "5-a1+b2c3d", return "5123"
+     * 8. filter "+5-a1+b2c3d", return "+5123"
+     * 9. filter Spanned("5-a1-b2c3d"), return Spanned("5123") and copy spans.
+     * 10. filter "", return null
+     * 11. filter "-123456" but dest has '-' after dend, return ""
+     * 12. filter "-123456" but dest has '+' after dend, return ""
+     * 13. filter "-123456" but dest has '-' before dstart, return "123456"
+     * 14. filter "+123456" but dest has '-' before dstart, return "123456"
      */
     public void testFilter2() {
         String source = "-123456";
@@ -123,6 +135,11 @@
                 dest, 0, dest.length()));
         assertEquals(destString, dest.toString());
 
+        source = "+123456";
+        assertNull(digitsKeyListener.filter(source, 0, source.length(),
+                dest, 0, dest.length()));
+        assertEquals(destString, dest.toString());
+
         source = "-a1.b2c3d";
         assertEquals("-123", (digitsKeyListener.filter(source, 0, source.length(),
                 dest, 0, dest.length())).toString());
@@ -133,11 +150,27 @@
                 dest, 0, dest.length())).toString());
         assertEquals(destString, dest.toString());
 
+        source = "+a1-b2c3d";
+        assertEquals("+123", (digitsKeyListener.filter(source, 0, source.length(),
+                dest, 0, dest.length())).toString());
+        assertEquals(destString, dest.toString());
+
         source = "5-a1-b2c3d";
         assertEquals("5123", (digitsKeyListener.filter(source, 0, source.length(),
                 dest, 0, dest.length())).toString());
         assertEquals(destString, dest.toString());
 
+        source = "5-a1+b2c3d";
+        assertEquals("5123", (digitsKeyListener.filter(source, 0, source.length(),
+                dest, 0, dest.length())).toString());
+        assertEquals(destString, dest.toString());
+
+        source = "+5-a1+b2c3d";
+        assertEquals("+5123", (digitsKeyListener.filter(source, 0, source.length(),
+                dest, 0, dest.length())).toString());
+        assertEquals(destString, dest.toString());
+
+        source = "5-a1+b2c3d";
         Object what = new Object();
         Spannable spannableSource = new SpannableString(source);
         spannableSource.setSpan(what, 0, spannableSource.length(), Spanned.SPAN_POINT_POINT);
@@ -158,11 +191,23 @@
                 dest, 0, dest.length() - 1)).toString());
         assertEquals(endSign, dest.toString());
 
+        endSign = "789+";
+        dest = new SpannableString(endSign);
+        assertEquals("", (digitsKeyListener.filter(source, 0, source.length(),
+                dest, 0, dest.length() - 1)).toString());
+        assertEquals(endSign, dest.toString());
+
         String startSign = "-789";
         dest = new SpannableString(startSign);
         assertEquals("123456", (digitsKeyListener.filter(source, 0, source.length(),
                 dest, 1, dest.length())).toString());
         assertEquals(startSign, dest.toString());
+
+        source = "+123456";
+        dest = new SpannableString(startSign);
+        assertEquals("123456", (digitsKeyListener.filter(source, 0, source.length(),
+                dest, 1, dest.length())).toString());
+        assertEquals(startSign, dest.toString());
     }
 
     /**
@@ -170,12 +215,13 @@
      * Current accepted characters are '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.'.
      * 1. filter "123.456", return null
      * 2. filter "-a1.b2c3d", return "1.23"
-     * 3. filter "a1.b2c3d.", return "123."
-     * 4. filter "5.a1.b2c3d", return "51.23"
-     * 5. filter Spanned("5.a1.b2c3d"), return Spanned("51.23") and copy spans.
-     * 6. filter "", return null
-     * 7. filter "123.456" but dest has '.' after dend, return "123456"
-     * 8. filter "123.456" but dest has '.' before dstart, return "123456"
+     * 3. filter "+a1.b2c3d", return "1.23"
+     * 4. filter "a1.b2c3d.", return "123."
+     * 5. filter "5.a1.b2c3d", return "51.23"
+     * 6. filter Spanned("5.a1.b2c3d"), return Spanned("51.23") and copy spans.
+     * 7. filter "", return null
+     * 8. filter "123.456" but dest has '.' after dend, return "123456"
+     * 9. filter "123.456" but dest has '.' before dstart, return "123456"
      */
     public void testFilter3() {
         String source = "123.456";
@@ -192,6 +238,11 @@
                 dest, 0, dest.length())).toString());
         assertEquals(destString, dest.toString());
 
+        source = "+a1.b2c3d";
+        assertEquals("1.23", (digitsKeyListener.filter(source, 0, source.length(),
+                dest, 0, dest.length())).toString());
+        assertEquals(destString, dest.toString());
+
         source = "a1.b2c3d.";
         assertEquals("123.", (digitsKeyListener.filter(source, 0, source.length(),
                 dest, 0, dest.length())).toString());
@@ -231,17 +282,25 @@
 
     /**
      * Check point:
-     * Current accepted characters are '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '-'.
+     * Current accepted characters are '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '-',
+     * '+'.
      * 1. filter "-123.456", return null
-     * 2. filter "-a1.b2c3d", return "-1.23"
-     * 3. filter "a1.b-2c3d.", return "123."
-     * 4. filter "-5.a1.b2c3d", return "-51.23"
-     * 5. filter Spanned("-5.a1.b2c3d"), return Spanned("-51.23") and copy spans.
-     * 6. filter "", return null
-     * 7. filter "-123.456" but dest has '.' after dend, return "-123456"
-     * 8. filter "-123.456" but dest has '.' before dstart, return "123456"
-     * 9. filter "-123.456" but dest has '-' after dend, return ""
-     * 10. filter "-123.456" but dest has '-' before dstart, return "123.456"
+     * 2. filter "+123.456", return null
+     * 3. filter "-a1.b2c3d", return "-1.23"
+     * 4. filter "+a1.b2c3d", return "+1.23"
+     * 5. filter "a1.b-2c+3d.", return "123."
+     * 6. filter "-5.a1.b2c+3d", return "-51.23"
+     * 7. filter "+5.a1.b2c-3d", return "+51.23"
+     * 8. filter Spanned("-5.a1.b2c3d"), return Spanned("-51.23") and copy spans.
+     * 9. filter "", return null
+     * 10. filter "-123.456" but dest has '.' after dend, return "-123456"
+     * 11. filter "-123.456" but dest has '.' before dstart, return "123456"
+     * 12. filter "+123.456" but dest has '.' after dend, return "+123456"
+     * 13. filter "+123.456" but dest has '.' before dstart, return "123456"
+     * 14. filter "-123.456" but dest has '-' after dend, return ""
+     * 15. filter "-123.456" but dest has '+' after dend, return ""
+     * 16. filter "-123.456" but dest has '-' before dstart, return "123.456"
+     * 17. filter "+123.456" but dest has '-' before dstart, return "123.456"
      */
     public void testFilter4() {
         String source = "-123.456";
@@ -253,21 +312,32 @@
                 dest, 0, dest.length()));
         assertEquals(destString, dest.toString());
 
+        source = "+123.456";
+        assertNull(digitsKeyListener.filter(source, 0, source.length(),
+                dest, 0, dest.length()));
+        assertEquals(destString, dest.toString());
+
         source = "-a1.b2c3d";
         assertEquals("-1.23", (digitsKeyListener.filter(source, 0, source.length(),
                 dest, 0, dest.length())).toString());
         assertEquals(destString, dest.toString());
 
-        source = "a1.b-2c3d.";
+        source = "a1.b-2c+3d.";
         assertEquals("123.", (digitsKeyListener.filter(source, 0, source.length(),
                 dest, 0, dest.length())).toString());
         assertEquals(destString, dest.toString());
 
-        source = "-5.a1.b2c3d";
+        source = "-5.a1.b2c+3d";
         assertEquals("-51.23", (digitsKeyListener.filter(source, 0, source.length(),
                 dest, 0, dest.length())).toString());
         assertEquals(destString, dest.toString());
 
+        source = "+5.a1.b2c-3d";
+        assertEquals("+51.23", (digitsKeyListener.filter(source, 0, source.length(),
+                dest, 0, dest.length())).toString());
+        assertEquals(destString, dest.toString());
+
+        source = "-5.a1.b2c+3d";
         Object what = new Object();
         Spannable spannableSource = new SpannableString(source);
         spannableSource.setSpan(what, 0, spannableSource.length(), Spanned.SPAN_POINT_POINT);
@@ -294,17 +364,43 @@
                 dest, 1, dest.length())).toString());
         assertEquals(startDecimal, dest.toString());
 
+        source = "+123.456";
+        endDecimal = "789.";
+        dest = new SpannableString(endDecimal);
+        assertEquals("+123456", (digitsKeyListener.filter(source, 0, source.length(),
+                dest, 0, dest.length() - 1)).toString());
+        assertEquals(endDecimal, dest.toString());
+
+        startDecimal = ".789";
+        dest = new SpannableString(startDecimal);
+        assertEquals("123456", (digitsKeyListener.filter(source, 0, source.length(),
+                dest, 1, dest.length())).toString());
+        assertEquals(startDecimal, dest.toString());
+
+        source = "-123.456";
         String endSign = "789-";
         dest = new SpannableString(endSign);
         assertEquals("", (digitsKeyListener.filter(source, 0, source.length(),
                 dest, 0, dest.length() - 1)).toString());
         assertEquals(endSign, dest.toString());
 
+        endSign = "789+";
+        dest = new SpannableString(endSign);
+        assertEquals("", (digitsKeyListener.filter(source, 0, source.length(),
+                dest, 0, dest.length() - 1)).toString());
+        assertEquals(endSign, dest.toString());
+
         String startSign = "-789";
         dest = new SpannableString(startSign);
         assertEquals("123.456", (digitsKeyListener.filter(source, 0, source.length(),
                 dest, 1, dest.length())).toString());
         assertEquals(startSign, dest.toString());
+
+        source = "+123.456";
+        dest = new SpannableString(startSign);
+        assertEquals("123.456", (digitsKeyListener.filter(source, 0, source.length(),
+                dest, 1, dest.length())).toString());
+        assertEquals(startSign, dest.toString());
     }
 
     /**
@@ -346,12 +442,13 @@
 
     /**
      * Scenario description:
-     * Current accepted characters are '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-'.
+     * Current accepted characters are '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '+'.
      *  1. Press '-' key and check if the content of TextView becomes "-"
      *  2. Press '1' key and check if the content of TextView becomes "-1"
      *  3. Press '.' key and this key could not be accepted.
-     *  4. Press '2' key and check if the content of TextView becomes "-12"
-     *  5. Press '-' key and this key could not be accepted,
+     *  4. Press '+' key and this key could not be accepted.
+     *  5. Press '2' key and check if the content of TextView becomes "-12"
+     *  6. Press '-' key and this key could not be accepted,
      *     because text view accepts minus sign iff it at the beginning.
      */
     public void testDigitsKeyListener2() {
@@ -378,6 +475,10 @@
         sendKeys(KeyEvent.KEYCODE_PERIOD);
         assertEquals("-1", mTextView.getText().toString());
 
+        // press '+' key.
+        sendKeys(KeyEvent.KEYCODE_PLUS);
+        assertEquals("-1", mTextView.getText().toString());
+
         // press '2' key.
         sendKeys(KeyEvent.KEYCODE_2);
         assertEquals("-12", mTextView.getText().toString());
@@ -391,92 +492,98 @@
      * Scenario description:
      * Current accepted characters are '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.'.
      *  1. Press '-' key and check if the content of TextView becomes ""
-     *  2. Press '1' key and check if the content of TextView becomes "1"
-     *  3. Press '.' key and check if the content of TextView becomes "1."
-     *  4. Press '2' key and check if the content of TextView becomes "1.2"
-     *  5. Press '.' key and this key could not be accepted,
+     *  2. Press '+' key and check if the content of TextView becomes ""
+     *  3. Press '1' key and check if the content of TextView becomes "1"
+     *  4. Press '.' key and check if the content of TextView becomes "1."
+     *  5. Press '2' key and check if the content of TextView becomes "1.2"
+     *  6. Press '.' key and this key could not be accepted,
      *     because text view accepts only one decimal point per field.
      */
     public void testDigitsKeyListener3() {
-//        final DigitsKeyListener digitsKeyListener = DigitsKeyListener.getInstance(false, true);
-//
-//        mActivity.runOnUiThread(new Runnable() {
-//            public void run() {
-//                mTextView.setKeyListener(digitsKeyListener);
-//                mTextView.requestFocus();
-//            }
-//        });
-//        mInstrumentation.waitForIdleSync();
-//        assertEquals("", mTextView.getText().toString());
-//
-//        // press '-' key.
-//        sendKeys(KeyEvent.KEYCODE_MINUS);
-//        assertEquals("", mTextView.getText().toString());
-//
-//        // press '1' key.
-//        sendKeys(KeyEvent.KEYCODE_1);
-//        assertEquals("1", mTextView.getText().toString());
-//
-//        // press '.' key.
-//        sendKeys(KeyEvent.KEYCODE_PERIOD);
-//        assertEquals("1.", mTextView.getText().toString());
-//
-//        // press '2' key.
-//        sendKeys(KeyEvent.KEYCODE_2);
-//        assertEquals("1.2", mTextView.getText().toString());
-//
-//        // press '.' key.
-//        sendKeys(KeyEvent.KEYCODE_PERIOD);
-//        assertEquals("1.2", mTextView.getText().toString());
+        final DigitsKeyListener digitsKeyListener = DigitsKeyListener.getInstance(false, true);
+
+        mActivity.runOnUiThread(new Runnable() {
+            public void run() {
+                mTextView.setKeyListener(digitsKeyListener);
+                mTextView.requestFocus();
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        assertEquals("", mTextView.getText().toString());
+
+        // press '-' key.
+        sendKeys(KeyEvent.KEYCODE_MINUS);
+        assertEquals("", mTextView.getText().toString());
+
+        // press '+' key.
+        sendKeys(KeyEvent.KEYCODE_PLUS);
+        assertEquals("", mTextView.getText().toString());
+
+        // press '1' key.
+        sendKeys(KeyEvent.KEYCODE_1);
+        assertEquals("1", mTextView.getText().toString());
+
+        // press '.' key.
+        sendKeys(KeyEvent.KEYCODE_PERIOD);
+        assertEquals("1.", mTextView.getText().toString());
+
+        // press '2' key.
+        sendKeys(KeyEvent.KEYCODE_2);
+        assertEquals("1.2", mTextView.getText().toString());
+
+        // press '.' key.
+        sendKeys(KeyEvent.KEYCODE_PERIOD);
+        assertEquals("1.2", mTextView.getText().toString());
     }
 
     /**
      * Scenario description:
-     * Current accepted characters are '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '.'.
-     *  1. Press '-' key and check if the content of TextView becomes "-"
-     *  2. Press '1' key and check if the content of TextView becomes "-1"
+     * Current accepted characters are '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '+',
+     * '.'.
+     *  1. Press '+' key and check if the content of TextView becomes "+"
+     *  2. Press '1' key and check if the content of TextView becomes "+1"
      *  3. Press '.' key and this key could not be accepted.
-     *  4. Press '2' key and check if the content of TextView becomes "-12"
+     *  4. Press '2' key and check if the content of TextView becomes "+12"
      *  5. Press '-' key and this key could not be accepted,
      *     because text view accepts minus sign iff it at the beginning.
      *  6. Press '.' key and this key could not be accepted,
      *     because text view accepts only one decimal point per field.
      */
     public void testDigitsKeyListener4() {
-//        final DigitsKeyListener digitsKeyListener = DigitsKeyListener.getInstance(true, true);
-//
-//        mActivity.runOnUiThread(new Runnable() {
-//            public void run() {
-//                mTextView.setKeyListener(digitsKeyListener);
-//                mTextView.requestFocus();
-//            }
-//        });
-//        mInstrumentation.waitForIdleSync();
-//        assertEquals("", mTextView.getText().toString());
-//
-//        // press '-' key.
-//        sendKeys(KeyEvent.KEYCODE_MINUS);
-//        assertEquals("-", mTextView.getText().toString());
-//
-//        // press '1' key.
-//        sendKeys(KeyEvent.KEYCODE_1);
-//        assertEquals("-1", mTextView.getText().toString());
-//
-//        // press '.' key.
-//        sendKeys(KeyEvent.KEYCODE_PERIOD);
-//        assertEquals("-1.", mTextView.getText().toString());
-//
-//        // press '2' key.
-//        sendKeys(KeyEvent.KEYCODE_2);
-//        assertEquals("-1.2", mTextView.getText().toString());
-//
-//        // press '-' key.
-//        sendKeys(KeyEvent.KEYCODE_MINUS);
-//        assertEquals("-1.2", mTextView.getText().toString());
-//
-//        // press '.' key.
-//        sendKeys(KeyEvent.KEYCODE_PERIOD);
-//        assertEquals("-1.2", mTextView.getText().toString());
+        final DigitsKeyListener digitsKeyListener = DigitsKeyListener.getInstance(true, true);
+
+        mActivity.runOnUiThread(new Runnable() {
+            public void run() {
+                mTextView.setKeyListener(digitsKeyListener);
+                mTextView.requestFocus();
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        assertEquals("", mTextView.getText().toString());
+
+        // press '+' key.
+        sendKeys(KeyEvent.KEYCODE_PLUS);
+        assertEquals("+", mTextView.getText().toString());
+
+        // press '1' key.
+        sendKeys(KeyEvent.KEYCODE_1);
+        assertEquals("+1", mTextView.getText().toString());
+
+        // press '.' key.
+        sendKeys(KeyEvent.KEYCODE_PERIOD);
+        assertEquals("+1.", mTextView.getText().toString());
+
+        // press '2' key.
+        sendKeys(KeyEvent.KEYCODE_2);
+        assertEquals("+1.2", mTextView.getText().toString());
+
+        // press '-' key.
+        sendKeys(KeyEvent.KEYCODE_MINUS);
+        assertEquals("+1.2", mTextView.getText().toString());
+
+        // press '.' key.
+        sendKeys(KeyEvent.KEYCODE_PERIOD);
+        assertEquals("+1.2", mTextView.getText().toString());
     }
 
     /**
@@ -570,9 +677,9 @@
 
         final char[][] expected = new char[][] {
             new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' },
-            new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-' },
+            new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '+' },
             new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.' },
-            new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '.' },
+            new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '+', '.' },
         };
 
         TextMethodUtils.assertEquals(expected[0],
diff --git a/tests/tests/view/src/android/view/cts/DisplayTest.java b/tests/tests/view/src/android/view/cts/DisplayTest.java
deleted file mode 100644
index 489282a..0000000
--- a/tests/tests/view/src/android/view/cts/DisplayTest.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.cts;
-
-import android.content.Context;
-import android.test.AndroidTestCase;
-import android.util.DisplayMetrics;
-import android.view.Display;
-import android.view.WindowManager;
-
-public class DisplayTest extends AndroidTestCase {
-
-    /**
-     * Test the properties of Display, they are:
-     * 1 index of this display
-     * 2 height of this display in pixels
-     * 3 width of this display in pixels
-     * 4 orientation of this display
-     * 5 pixel format of this display
-     * 6 refresh rate of this display in frames per second
-     * 7 Initialize a DisplayMetrics object from this display's data
-     */
-    public void testGetDisplayAttrs() {
-        Context con = getContext();
-        WindowManager windowManager = (WindowManager) con.getSystemService(Context.WINDOW_SERVICE);
-        Display display = windowManager.getDefaultDisplay();
-
-        assertEquals(Display.DEFAULT_DISPLAY, display.getDisplayId());
-        assertTrue(0 < display.getHeight());
-        assertTrue(0 < display.getWidth());
-        display.getOrientation();
-        assertTrue(0 < display.getPixelFormat());
-        assertTrue(0 < display.getRefreshRate());
-
-        DisplayMetrics outMetrics = new DisplayMetrics();
-        outMetrics.setToDefaults();
-        display.getMetrics(outMetrics);
-        assertEquals(display.getHeight(), outMetrics.heightPixels);
-        assertEquals(display.getWidth(), outMetrics.widthPixels);
-
-        // The scale is in [0.1, 3], and density is the scale factor.
-        assertTrue(0.1f <= outMetrics.density && outMetrics.density <= 3.0f);
-        assertTrue(0.1f <= outMetrics.scaledDensity && outMetrics.density <= 3.0f);
-        assertTrue(0 < outMetrics.xdpi);
-        assertTrue(0 < outMetrics.ydpi);
-    }
-}
diff --git a/tests/tests/view/src/android/view/cts/ViewTreeObserverTest.java b/tests/tests/view/src/android/view/cts/ViewTreeObserverTest.java
index c5b56a4..1ee690b 100644
--- a/tests/tests/view/src/android/view/cts/ViewTreeObserverTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewTreeObserverTest.java
@@ -35,7 +35,6 @@
 import android.view.ViewTreeObserver.OnTouchModeChangeListener;
 import android.widget.Button;
 import android.widget.LinearLayout;
-import android.widget.ListView;
 import android.widget.ScrollView;
 
 public class ViewTreeObserverTest extends ActivityInstrumentationTestCase2<MockActivity> {
@@ -69,8 +68,15 @@
 
     public void testAddOnGlobalFocusChangeListener() {
         final LinearLayout layout = (LinearLayout) mActivity.findViewById(R.id.linearlayout);
-        final ListView lv1 = (ListView) mActivity.findViewById(R.id.listview1);
-        final ListView lv2 = (ListView) mActivity.findViewById(R.id.listview2);
+        final View view1 = mActivity.findViewById(R.id.view1);
+        final View view2 = mActivity.findViewById(R.id.view2);
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                view1.requestFocus();
+            }
+        });
 
         mViewTreeObserver = layout.getViewTreeObserver();
         final MockOnGlobalFocusChangeListener listener = new MockOnGlobalFocusChangeListener();
@@ -80,14 +86,15 @@
         mInstrumentation.runOnMainSync(new Runnable() {
             @Override
             public void run() {
-                layout.requestChildFocus(lv1, lv2);
+                view2.requestFocus();
             }
         });
         mInstrumentation.waitForIdleSync();
         new PollingCheck() {
             @Override
             protected boolean check() {
-                return listener.hasCalledOnGlobalFocusChanged();
+                return listener.hasCalledOnGlobalFocusChanged()
+                        && listener.oldFocus == view1 && listener.newFocus == view2;
             }
         }.run();
     }
@@ -144,16 +151,16 @@
     }
 
     public void testAddOnComputeInternalInsetsListener() {
-        final ListView lv1 = (ListView) mActivity.findViewById(R.id.listview1);
-        mViewTreeObserver = lv1.getViewTreeObserver();
+        final View view1 = mActivity.findViewById(R.id.view1);
+        mViewTreeObserver = view1.getViewTreeObserver();
 
         MockOnComputeInternalInsetsListener listener = new MockOnComputeInternalInsetsListener();
         mViewTreeObserver.addOnComputeInternalInsetsListener(listener);
     }
 
     public void testRemoveOnComputeInternalInsetsListener() {
-        final ListView lv1 = (ListView) mActivity.findViewById(R.id.listview1);
-        mViewTreeObserver = lv1.getViewTreeObserver();
+        final View view1 = mActivity.findViewById(R.id.view1);
+        mViewTreeObserver = view1.getViewTreeObserver();
 
         MockOnComputeInternalInsetsListener listener = new MockOnComputeInternalInsetsListener();
         mViewTreeObserver.removeOnComputeInternalInsetsListener(listener);
@@ -207,8 +214,15 @@
 
     public void testRemoveOnGlobalFocusChangeListener() {
         final LinearLayout layout = (LinearLayout) mActivity.findViewById(R.id.linearlayout);
-        final ListView lv1 = (ListView) mActivity.findViewById(R.id.listview1);
-        final ListView lv2 = (ListView) mActivity.findViewById(R.id.listview2);
+        final View view1 = mActivity.findViewById(R.id.view1);
+        final View view2 = mActivity.findViewById(R.id.view2);
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                view1.requestFocus();
+            }
+        });
 
         mViewTreeObserver = layout.getViewTreeObserver();
         final MockOnGlobalFocusChangeListener listener = new MockOnGlobalFocusChangeListener();
@@ -217,14 +231,15 @@
         mInstrumentation.runOnMainSync(new Runnable() {
             @Override
             public void run() {
-                layout.requestChildFocus(lv1, lv2);
+                view2.requestFocus();
             }
         });
         mInstrumentation.waitForIdleSync();
         new PollingCheck() {
             @Override
             protected boolean check() {
-                return listener.hasCalledOnGlobalFocusChanged();
+                return listener.hasCalledOnGlobalFocusChanged()
+                        && listener.oldFocus == view1 && listener.newFocus == view2;
             }
         }.run();
 
@@ -234,7 +249,7 @@
         mInstrumentation.runOnMainSync(new Runnable() {
             @Override
             public void run() {
-                layout.requestChildFocus(lv1, lv2);
+                view1.requestFocus();
             }
         });
         mInstrumentation.waitForIdleSync();
@@ -345,10 +360,14 @@
 
     private class MockOnGlobalFocusChangeListener implements OnGlobalFocusChangeListener {
         private boolean mCalledOnGlobalFocusChanged = false;
+        View oldFocus;
+        View newFocus;
 
         @Override
         public void onGlobalFocusChanged(View oldFocus, View newFocus) {
             mCalledOnGlobalFocusChanged = true;
+            this.oldFocus = oldFocus;
+            this.newFocus = newFocus;
         }
 
         public boolean hasCalledOnGlobalFocusChanged() {
@@ -357,6 +376,8 @@
 
         public void reset() {
             mCalledOnGlobalFocusChanged = false;
+            oldFocus = null;
+            newFocus = null;
         }
     }
 
diff --git a/tests/tests/view/src/android/view/inputmethod/cts/InputMethodInfoTest.java b/tests/tests/view/src/android/view/inputmethod/cts/InputMethodInfoTest.java
index 00a8dea..722c1b3 100644
--- a/tests/tests/view/src/android/view/inputmethod/cts/InputMethodInfoTest.java
+++ b/tests/tests/view/src/android/view/inputmethod/cts/InputMethodInfoTest.java
@@ -16,7 +16,6 @@
 
 package android.view.inputmethod.cts;
 
-
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -27,6 +26,7 @@
 import android.util.Printer;
 import android.view.inputmethod.InputMethod;
 import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
 
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -40,14 +40,40 @@
     private CharSequence mLabel;
     private String mSettingsActivity;
 
+    private int mSubtypeNameResId;
+    private int mSubtypeIconResId;
+    private String mSubtypeLocale;
+    private String mSubtypeMode;
+    private String mSubtypeExtraValue_key;
+    private String mSubtypeExtraValue_value;
+    private String mSubtypeExtraValue;
+    private boolean mSubtypeIsAuxiliary;
+    private boolean mSubtypeOverridesImplicitlyEnabledSubtype;
+    private int mSubtypeId;
+    private InputMethodSubtype mInputMethodSubtype;
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
         mPackageName = mContext.getPackageName();
-        mClassName = InputMethodInfoStub.class.getName();
+        mClassName = InputMethodSettingsActivityStub.class.getName();
         mLabel = "test";
-        mSettingsActivity = "android.view.inputmethod.cts.InputMethodInfoStub";
+        mSettingsActivity = "android.view.inputmethod.cts.InputMethodSettingsActivityStub";
         mInputMethodInfo = new InputMethodInfo(mPackageName, mClassName, mLabel, mSettingsActivity);
+
+        mSubtypeNameResId = 0;
+        mSubtypeIconResId = 0;
+        mSubtypeLocale = "en_US";
+        mSubtypeMode = "keyboard";
+        mSubtypeExtraValue_key = "key1";
+        mSubtypeExtraValue_value = "value1";
+        mSubtypeExtraValue = "tag," + mSubtypeExtraValue_key + "=" + mSubtypeExtraValue_value;
+        mSubtypeIsAuxiliary = false;
+        mSubtypeOverridesImplicitlyEnabledSubtype = false;
+        mSubtypeId = 99;
+        mInputMethodSubtype = new InputMethodSubtype(mSubtypeNameResId, mSubtypeIconResId,
+                mSubtypeLocale, mSubtypeMode, mSubtypeExtraValue, mSubtypeIsAuxiliary,
+                mSubtypeOverridesImplicitlyEnabledSubtype, mSubtypeId);
     }
 
     public void testInputMethodInfoProperties() throws XmlPullParserException, IOException {
@@ -58,7 +84,7 @@
         assertEquals(0, mInputMethodInfo.getIsDefaultResourceId());
 
         Intent intent = new Intent(InputMethod.SERVICE_INTERFACE);
-        intent.setClass(mContext, InputMethodInfoStub.class);
+        intent.setClass(mContext, InputMethodSettingsActivityStub.class);
         PackageManager pm = mContext.getPackageManager();
         List<ResolveInfo> ris = pm.queryIntentServices(intent, PackageManager.GET_META_DATA);
         for (int i = 0; i < ris.size(); i++) {
@@ -69,6 +95,22 @@
         }
     }
 
+    public void testInputMethodSubtypeProperties() {
+        // TODO: Test InputMethodSubtype.getDisplayName()
+        assertEquals(mSubtypeNameResId, mInputMethodSubtype.getNameResId());
+        assertEquals(mSubtypeIconResId, mInputMethodSubtype.getIconResId());
+        assertEquals(mSubtypeLocale, mInputMethodSubtype.getLocale());
+        assertEquals(mSubtypeMode, mInputMethodSubtype.getMode());
+        assertEquals(mSubtypeExtraValue, mInputMethodSubtype.getExtraValue());
+        assertTrue(mInputMethodSubtype.containsExtraValueKey(mSubtypeExtraValue_key));
+        assertEquals(mSubtypeExtraValue_value,
+                mInputMethodSubtype.getExtraValueOf(mSubtypeExtraValue_key));
+        assertEquals(mSubtypeIsAuxiliary, mInputMethodSubtype.isAuxiliary());
+        assertEquals(mSubtypeOverridesImplicitlyEnabledSubtype,
+                mInputMethodSubtype.overridesImplicitlyEnabledSubtype());
+        assertEquals(mSubtypeId, mInputMethodSubtype.hashCode());
+    }
+
     private void assertService(ServiceInfo expected, ServiceInfo actual) {
         assertEquals(expected.getIconResource(), actual.getIconResource());
         assertEquals(expected.labelRes, actual.labelRes);
@@ -110,19 +152,39 @@
         assertEquals(expected.toString(), mInputMethodInfo.loadLabel(pm).toString());
     }
 
-    public void testWriteToParcel() {
-        Parcel p = Parcel.obtain();
+    public void testInputMethodInfoWriteToParcel() {
+        final Parcel p = Parcel.obtain();
         mInputMethodInfo.writeToParcel(p, 0);
         p.setDataPosition(0);
-        InputMethodInfo inputMethodInfo = InputMethodInfo.CREATOR.createFromParcel(p);
+        final InputMethodInfo imi = InputMethodInfo.CREATOR.createFromParcel(p);
 
-        assertEquals(mInputMethodInfo.getPackageName(), inputMethodInfo.getPackageName());
-        assertEquals(mInputMethodInfo.getServiceName(), inputMethodInfo.getServiceName());
-        assertEquals(mInputMethodInfo.getSettingsActivity(), inputMethodInfo.getSettingsActivity());
-        assertEquals(mInputMethodInfo.getId(), inputMethodInfo.getId());
-        assertEquals(mInputMethodInfo.getIsDefaultResourceId(), inputMethodInfo
-                .getIsDefaultResourceId());
-        assertService(mInputMethodInfo.getServiceInfo(), inputMethodInfo.getServiceInfo());
+        assertEquals(mInputMethodInfo.getPackageName(), imi.getPackageName());
+        assertEquals(mInputMethodInfo.getServiceName(), imi.getServiceName());
+        assertEquals(mInputMethodInfo.getSettingsActivity(), imi.getSettingsActivity());
+        assertEquals(mInputMethodInfo.getId(), imi.getId());
+        assertEquals(mInputMethodInfo.getIsDefaultResourceId(), imi.getIsDefaultResourceId());
+        assertService(mInputMethodInfo.getServiceInfo(), imi.getServiceInfo());
+    }
+
+    public void testInputMethodSubtypeWriteToParcel() {
+        final Parcel p = Parcel.obtain();
+        mInputMethodSubtype.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        final InputMethodSubtype subtype = InputMethodSubtype.CREATOR.createFromParcel(p);
+
+        assertEquals(mInputMethodSubtype.containsExtraValueKey(mSubtypeExtraValue_key),
+                subtype.containsExtraValueKey(mSubtypeExtraValue_key));
+        assertEquals(mInputMethodSubtype.getExtraValue(), subtype.getExtraValue());
+        assertEquals(mInputMethodSubtype.getExtraValueOf(mSubtypeExtraValue_key),
+                subtype.getExtraValueOf(mSubtypeExtraValue_key));
+        assertEquals(mInputMethodSubtype.getIconResId(), subtype.getIconResId());
+        assertEquals(mInputMethodSubtype.getLocale(), subtype.getLocale());
+        assertEquals(mInputMethodSubtype.getMode(), subtype.getMode());
+        assertEquals(mInputMethodSubtype.getNameResId(), subtype.getNameResId());
+        assertEquals(mInputMethodSubtype.hashCode(), subtype.hashCode());
+        assertEquals(mInputMethodSubtype.isAuxiliary(), subtype.isAuxiliary());
+        assertEquals(mInputMethodSubtype.overridesImplicitlyEnabledSubtype(),
+                subtype.overridesImplicitlyEnabledSubtype());
     }
 
     class MockPrinter implements Printer {
diff --git a/tests/tests/webkit/AndroidManifest.xml b/tests/tests/webkit/AndroidManifest.xml
index 9475451..493762e 100644
--- a/tests/tests/webkit/AndroidManifest.xml
+++ b/tests/tests/webkit/AndroidManifest.xml
@@ -19,6 +19,8 @@
     package="com.android.cts.webkit">
 
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
+    <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>
     <application>
         <uses-library android:name="android.test.runner" />
     </application>
diff --git a/tests/tests/webkit/src/android/webkit/cts/CacheManagerTest.java b/tests/tests/webkit/src/android/webkit/cts/CacheManagerTest.java
deleted file mode 100644
index 66b1e02..0000000
--- a/tests/tests/webkit/src/android/webkit/cts/CacheManagerTest.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit.cts;
-
-import android.cts.util.PollingCheck;
-import android.test.ActivityInstrumentationTestCase2;
-import android.webkit.CacheManager;
-import android.webkit.CacheManager.CacheResult;
-
-
-import java.util.Map;
-
-public class CacheManagerTest extends ActivityInstrumentationTestCase2<WebViewStubActivity> {
-    private static final long CACHEMANAGER_INIT_TIMEOUT = 5000L;
-    private static final long NETWORK_OPERATION_TIMEOUT = 10000L;
-
-    private CtsTestServer mWebServer;
-    private WebViewOnUiThread mOnUiThread;
-
-    public CacheManagerTest() {
-        super("com.android.cts.stub", WebViewStubActivity.class);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mOnUiThread = new WebViewOnUiThread(this, getActivity().getWebView());
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        mOnUiThread.cleanUp();
-        if (mWebServer != null) {
-            mWebServer.shutdown();
-        }
-        super.tearDown();
-    }
-
-    public void testGetCacheFileBaseDir() {
-        assertTrue(CacheManager.getCacheFileBaseDir().exists());
-    }
-
-    public void testCacheTransaction() {
-    }
-
-    public void testCacheFile() throws Exception {
-        mWebServer = new CtsTestServer(getActivity());
-        final String url = mWebServer.getAssetUrl(TestHtmlConstants.EMBEDDED_IMG_URL);
-
-        // Wait for CacheManager#init() finish.
-        new PollingCheck(CACHEMANAGER_INIT_TIMEOUT) {
-            @Override
-            protected boolean check() {
-                return CacheManager.getCacheFileBaseDir() != null;
-            }
-        }.run();
-
-        mOnUiThread.clearCache(true);
-        new PollingCheck(NETWORK_OPERATION_TIMEOUT) {
-            @Override
-            protected boolean check() {
-                CacheResult result = CacheManager.getCacheFile(url, null);
-                return result == null;
-            }
-        }.run();
-
-        mOnUiThread.loadUrlAndWaitForCompletion(url);
-        new PollingCheck(NETWORK_OPERATION_TIMEOUT) {
-            @Override
-            protected boolean check() {
-                CacheResult result = CacheManager.getCacheFile(url, null);
-                return result != null;
-            }
-        }.run();
-
-        // Can not test saveCacheFile(), because the output stream is null and
-        // saveCacheFile() will throw a NullPointerException.  There is no
-        // public API to set the output stream.
-    }
-
-    public void testCacheDisabled() {
-        // The cache should always be enabled.
-        assertFalse(CacheManager.cacheDisabled());
-    }
-}
diff --git a/tests/tests/webkit/src/android/webkit/cts/CacheManager_CacheResultTest.java b/tests/tests/webkit/src/android/webkit/cts/CacheManager_CacheResultTest.java
deleted file mode 100755
index 0c2a51a..0000000
--- a/tests/tests/webkit/src/android/webkit/cts/CacheManager_CacheResultTest.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit.cts;
-
-import android.cts.util.PollingCheck;
-import android.test.ActivityInstrumentationTestCase2;
-import android.webkit.CacheManager;
-import android.webkit.CacheManager.CacheResult;
-import android.webkit.WebView;
-
-
-import org.apache.http.HttpStatus;
-import org.apache.http.impl.cookie.DateUtils;
-
-import java.io.File;
-import java.io.InputStream;
-import java.util.HashMap;
-import java.util.Map;
-
-public class CacheManager_CacheResultTest
-        extends ActivityInstrumentationTestCase2<WebViewStubActivity> {
-    private static final long NETWORK_OPERATION_TIMEOUT = 10000L;
-
-    private CtsTestServer mWebServer;
-    private WebViewOnUiThread mOnUiThread;
-
-    public CacheManager_CacheResultTest() {
-        super("com.android.cts.stub", WebViewStubActivity.class);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mOnUiThread = new WebViewOnUiThread(this, getActivity().getWebView());
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        mOnUiThread.cleanUp();
-        if (mWebServer != null) {
-            mWebServer.shutdown();
-        }
-        super.tearDown();
-    }
-
-    public void testCacheResult() throws Exception {
-        final long validity = 5 * 60 * 1000; // 5 min
-        final long age = 30 * 60 * 1000; // 30 min
-        final long tolerance = 5 * 1000; // 5s
-
-        mWebServer = new CtsTestServer(getActivity());
-        final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
-        mWebServer.setDocumentAge(age);
-        mWebServer.setDocumentValidity(validity);
-
-        mOnUiThread.clearCache(true);
-        new PollingCheck(NETWORK_OPERATION_TIMEOUT) {
-            @Override
-            protected boolean check() {
-                CacheResult result =
-                    CacheManager.getCacheFile(url, null);
-                return result == null;
-            }
-        }.run();
-        final long time = System.currentTimeMillis();
-        mOnUiThread.loadUrlAndWaitForCompletion(url);
-
-        Map<String, String> headers = new HashMap<String, String>();
-        CacheResult result = CacheManager.getCacheFile(url, headers);
-        assertTrue(headers.isEmpty());
-
-        assertNotNull(result);
-        assertNotNull(result.getInputStream());
-        assertTrue(result.getContentLength() > 0);
-        assertNull(result.getETag());
-        assertEquals((double)(time - age),
-                (double)DateUtils.parseDate(result.getLastModified()).getTime(),
-                (double)tolerance);
-        File file = new File(CacheManager.getCacheFileBaseDir().getPath(), result.getLocalPath());
-        assertTrue(file.exists());
-        assertNull(result.getLocation());
-        assertEquals("text/html", result.getMimeType());
-        assertNull(result.getOutputStream());
-        assertEquals((double)(time + validity), (double)result.getExpires(),
-                (double)tolerance);
-        assertEquals(HttpStatus.SC_OK, result.getHttpStatusCode());
-        assertNotNull(result.getEncoding());
-
-        result.setEncoding("iso-8859-1");
-        assertEquals("iso-8859-1", result.getEncoding());
-
-        result.setInputStream(null);
-        assertNull(result.getInputStream());
-    }
-}
diff --git a/tests/tests/webkit/src/android/webkit/cts/CookieManagerTest.java b/tests/tests/webkit/src/android/webkit/cts/CookieManagerTest.java
index 51eae48..a0b4fa6 100755
--- a/tests/tests/webkit/src/android/webkit/cts/CookieManagerTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/CookieManagerTest.java
@@ -68,7 +68,7 @@
         CtsTestServer server = new CtsTestServer(getActivity(), false);
         String url = server.getCookieUrl("conquest.html");
         mOnUiThread.loadUrlAndWaitForCompletion(url);
-        assertEquals(null, mOnUiThread.getTitle()); // no cookies passed
+        assertEquals("0", mOnUiThread.getTitle()); // no cookies passed
         Thread.sleep(500);
         assertNull(mCookieManager.getCookie(url));
 
@@ -77,7 +77,7 @@
 
         url = server.getCookieUrl("war.html");
         mOnUiThread.loadUrlAndWaitForCompletion(url);
-        assertEquals(null, mOnUiThread.getTitle()); // no cookies passed
+        assertEquals("0", mOnUiThread.getTitle()); // no cookies passed
         waitForCookie(url);
         String cookie = mCookieManager.getCookie(url);
         assertNotNull(cookie);
@@ -89,7 +89,7 @@
 
         url = server.getCookieUrl("famine.html");
         mOnUiThread.loadUrlAndWaitForCompletion(url);
-        assertEquals("count=0", mOnUiThread.getTitle()); // outgoing cookie
+        assertEquals("1|count=0", mOnUiThread.getTitle()); // outgoing cookie
         waitForCookie(url);
         cookie = mCookieManager.getCookie(url);
         assertNotNull(cookie);
@@ -100,7 +100,7 @@
         url = server.getCookieUrl("death.html");
         mCookieManager.setCookie(url, "count=41");
         mOnUiThread.loadUrlAndWaitForCompletion(url);
-        assertEquals("count=41", mOnUiThread.getTitle()); // outgoing cookie
+        assertEquals("1|count=41", mOnUiThread.getTitle()); // outgoing cookie
         waitForCookie(url);
         cookie = mCookieManager.getCookie(url);
         assertNotNull(cookie);
diff --git a/tests/tests/webkit/src/android/webkit/cts/CookieTest.java b/tests/tests/webkit/src/android/webkit/cts/CookieTest.java
index 14a14d0..0c0396a 100644
--- a/tests/tests/webkit/src/android/webkit/cts/CookieTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/CookieTest.java
@@ -45,23 +45,6 @@
             }
         }
         assertFalse(mCookieManager.hasCookies());
-
-    }
-
-    public void testParse() {
-        String url = "http://www.foo.com";
-
-        // basic
-        mCookieManager.setCookie(url, "a=b");
-        String cookie = mCookieManager.getCookie(url);
-        assertTrue(cookie.equals("a=b"));
-
-        // quoted
-        mCookieManager.setCookie(url, "c=\"d;\"");
-        cookie = mCookieManager.getCookie(url);
-
-        assertTrue(cookie.contains("a=b"));
-        assertTrue(cookie.contains("c=\"d;\""));
     }
 
     public void testDomain() {
diff --git a/tests/tests/webkit/src/android/webkit/cts/GeolocationTest.java b/tests/tests/webkit/src/android/webkit/cts/GeolocationTest.java
new file mode 100644
index 0000000..16942e5
--- /dev/null
+++ b/tests/tests/webkit/src/android/webkit/cts/GeolocationTest.java
@@ -0,0 +1,473 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit.cts;
+
+import android.content.Context;
+import android.cts.util.PollingCheck;
+import android.graphics.Bitmap;
+import android.location.Criteria;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.location.LocationProvider;
+import android.os.Bundle;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+import android.webkit.CookieManager;
+import android.webkit.CookieSyncManager;
+import android.webkit.GeolocationPermissions;
+import android.webkit.JavascriptInterface;
+import android.webkit.WebChromeClient;
+import android.webkit.WebResourceResponse;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.webkit.cts.WebViewOnUiThread.WaitForLoadedClient;
+import android.webkit.cts.WebViewOnUiThread.WaitForProgressClient;
+
+import java.io.ByteArrayInputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.concurrent.Callable;
+import java.util.Date;
+import java.util.Random;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.Set;
+import java.util.TreeSet;
+
+import junit.framework.Assert;
+
+public class GeolocationTest extends ActivityInstrumentationTestCase2<WebViewStubActivity> {
+
+    // TODO Write additional tests to cover:
+    // - test that the errors are correct
+    // - test that use of gps and network location is correct
+
+    // The URLs does not matter since the tests will intercept the load, but it has to be a real
+    // url, and different domains.
+    private static final String URL_1 = "http://www.example.com";
+    private static final String URL_2 = "http://www.example.org";
+
+    private static final String JS_INTERFACE_NAME = "Android";
+    private static final int POLLING_TIMEOUT = 2000;
+
+    // static HTML page always injected instead of the url loaded
+    private static final String RAW_HTML =
+            "<!DOCTYPE html>\n" +
+            "<html>\n" +
+            "  <head>\n" +
+            "    <title>Geolocation</title>\n" +
+            "    <script>\n" +
+            "      function gotPos(position) {\n" +
+            "        " + JS_INTERFACE_NAME + ".gotLocation();\n" +
+            "      }\n" +
+            "      function initiate_getCurrentPosition() {\n" +
+            "        navigator.geolocation.getCurrentPosition(\n" +
+            "            gotPos,\n" +
+            "            handle_errors,\n" +
+            "            {maximumAge:1000});\n" +
+            "      }\n" +
+            "      function handle_errors(error) {\n" +
+            "        switch(error.code) {\n" +
+            "          case error.PERMISSION_DENIED:\n" +
+            "            " + JS_INTERFACE_NAME + ".errorDenied(); break;\n" +
+            "          case error.POSITION_UNAVAILABLE:\n" +
+            "            " + JS_INTERFACE_NAME + ".errorUnavailable(); break;\n" +
+            "          case error.TIMEOUT:\n" +
+            "            " + JS_INTERFACE_NAME + ".errorTimeout(); break;\n" +
+            "          default: break;\n" +
+            "        }\n" +
+            "      }\n" +
+            "    </script>\n" +
+            "  </head>\n" +
+            "  <body onload=\"initiate_getCurrentPosition();\">\n" +
+            "  </body>\n" +
+            "</html>";
+
+    private JavascriptStatusReceiver mJavascriptStatusReceiver;
+    private LocationManager mLocationManager;
+    private WebViewOnUiThread mOnUiThread;
+
+    public GeolocationTest() throws Exception {
+        super("com.android.cts.stub", WebViewStubActivity.class);
+    }
+
+    // Both this test and WebViewOnUiThread need to override some of the methods on WebViewClient,
+    // so this test sublclasses the WebViewClient from WebViewOnUiThread
+    private static class InterceptClient extends WaitForLoadedClient {
+
+        public InterceptClient(WebViewOnUiThread webViewOnUiThread) throws Exception {
+            super(webViewOnUiThread);
+        }
+
+        @Override
+        public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
+            // Intercept all page loads with the same geolocation enabled page
+            try {
+                return new WebResourceResponse("text/html", "utf-8",
+                    new ByteArrayInputStream(RAW_HTML.getBytes("UTF-8")));
+            } catch(java.io.UnsupportedEncodingException e) {
+                return null;
+            }
+        }
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        // Set up a WebView with JavaScript and Geolocation enabled
+        final String GEO_DIR = "geo_test";
+        mOnUiThread = new WebViewOnUiThread(this, getActivity().getWebView());
+        mOnUiThread.getSettings().setJavaScriptEnabled(true);
+        mOnUiThread.getSettings().setGeolocationEnabled(true);
+        mOnUiThread.getSettings().setGeolocationDatabasePath(
+                getActivity().getApplicationContext().getDir(GEO_DIR, 0).getPath());
+
+        // Add a JsInterface to report back to the test when a location is received
+        mJavascriptStatusReceiver = new JavascriptStatusReceiver();
+        mOnUiThread.addJavascriptInterface(mJavascriptStatusReceiver, JS_INTERFACE_NAME);
+
+        // Always intercept all loads with the same geolocation test page
+        mOnUiThread.setWebViewClient(new InterceptClient(mOnUiThread));
+        // Clear all permissions before each test
+        GeolocationPermissions.getInstance().clearAll();
+        // Cache this mostly because the lookup is two lines of code
+        mLocationManager = (LocationManager)getActivity().getApplicationContext()
+                .getSystemService(Context.LOCATION_SERVICE);
+        // Add a test provider before each test to inject a location
+        addTestProvider(LocationManager.NETWORK_PROVIDER);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        // Remove the test provider after each test
+        try {
+          mLocationManager.removeTestProvider(LocationManager.NETWORK_PROVIDER);
+        } catch (IllegalArgumentException e) {} // Not much to do about this
+        mOnUiThread.cleanUp();
+        // This will null all member and static variables
+        super.tearDown();
+    }
+
+    // Update location with a fixed latitude and longtitude, sets the time to the current time.
+    private void updateLocation(final String providerName) {
+        Location location = new Location(providerName);
+        location.setLatitude(40);
+        location.setLongitude(40);
+        location.setAccuracy(1.0f);
+        location.setTime(java.lang.System.currentTimeMillis());
+        location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
+        mLocationManager.setTestProviderLocation(providerName, location);
+    }
+
+    // Need to set the location just after loading the url. Setting it after each load instead of
+    // using a maximum age.
+    private void loadUrlAndUpdateLocation(String url) {
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        updateLocation(LocationManager.NETWORK_PROVIDER);
+    }
+
+    // WebChromeClient that accepts each location for one load. WebChromeClient is used in
+    // WebViewOnUiThread to detect when the page is loaded, so subclassing the one used there.
+    private static class TestSimpleGeolocationRequestWebChromeClient
+                extends WaitForProgressClient {
+        private boolean mReceivedRequest = false;
+        private final boolean mAccept;
+        private final boolean mRetain;
+
+        public TestSimpleGeolocationRequestWebChromeClient(
+                WebViewOnUiThread webViewOnUiThread, boolean accept, boolean retain) {
+            super(webViewOnUiThread);
+            this.mAccept = accept;
+            this.mRetain = retain;
+        }
+
+        @Override
+        public void onGeolocationPermissionsShowPrompt(
+                String origin, GeolocationPermissions.Callback callback) {
+            mReceivedRequest = true;
+            callback.invoke(origin, mAccept, mRetain);
+        }
+    }
+
+    private void addTestProvider(final String providerName) {
+        mLocationManager.addTestProvider(providerName,
+                true, //requiresNetwork,
+                false, // requiresSatellite,
+                true,  // requiresCell,
+                false, // hasMonetaryCost,
+                false, // supportsAltitude,
+                false, // supportsSpeed,
+                false, // supportsBearing,
+                Criteria.POWER_MEDIUM, // powerRequirement
+                Criteria.ACCURACY_FINE); // accuracy
+        mLocationManager.setTestProviderEnabled(providerName, true);
+    }
+
+    // Test loading a page and accepting the domain for one load
+    public void testSimpleGeolocationRequestAcceptOnce() throws Exception {
+        final TestSimpleGeolocationRequestWebChromeClient chromeClientAcceptOnce =
+                new TestSimpleGeolocationRequestWebChromeClient(mOnUiThread, true, false);
+        mOnUiThread.setWebChromeClient(chromeClientAcceptOnce);
+        loadUrlAndUpdateLocation(URL_1);
+        Callable<Boolean> receivedRequest = new Callable<Boolean>() {
+            @Override
+            public Boolean call() {
+                return chromeClientAcceptOnce.mReceivedRequest;
+            }
+        };
+        PollingCheck.check("Geolocation prompt not called", POLLING_TIMEOUT, receivedRequest);
+        Callable<Boolean> receivedLocation = new Callable<Boolean>() {
+            @Override
+            public Boolean call() {
+                return mJavascriptStatusReceiver.mHasPosition;
+            }
+        };
+        PollingCheck.check("JS didn't get position", POLLING_TIMEOUT, receivedLocation);
+        chromeClientAcceptOnce.mReceivedRequest = false;
+        // Load URL again, should receive callback again
+        loadUrlAndUpdateLocation(URL_1);
+        PollingCheck.check("Geolocation prompt not called", POLLING_TIMEOUT, receivedRequest);
+        PollingCheck.check("JS didn't get position", POLLING_TIMEOUT, receivedLocation);
+    }
+
+    // Class that waits and checks for a particular value being received
+    private static class ValueCheck<T> extends PollingCheck implements
+            android.webkit.ValueCallback<T> {
+        private boolean mReceived = false;
+        private final T mExpectedValue;
+        private T mReceivedValue = null;
+
+        public ValueCheck(T val) {
+            mExpectedValue = val;
+        }
+
+        @Override
+        protected boolean check() {
+            return mReceived && mExpectedValue.equals(mReceivedValue);
+        }
+        @Override
+        public void onReceiveValue(T value) {
+            mReceived = true;
+            mReceivedValue = value;
+        }
+    }
+
+    // Test loading a page and retaining the domain forever
+    public void testSimpleGeolocationRequestAcceptAlways() throws Exception {
+        final TestSimpleGeolocationRequestWebChromeClient chromeClientAcceptAlways =
+                new TestSimpleGeolocationRequestWebChromeClient(mOnUiThread, true, true);
+        mOnUiThread.setWebChromeClient(chromeClientAcceptAlways);
+        // Load url once, and the callback should accept the domain for all future loads
+        loadUrlAndUpdateLocation(URL_1);
+        Callable<Boolean> receivedRequest = new Callable<Boolean>() {
+            @Override
+            public Boolean call() {
+                return chromeClientAcceptAlways.mReceivedRequest;
+            }
+        };
+        PollingCheck.check("Geolocation prompt not called", POLLING_TIMEOUT, receivedRequest);
+        Callable<Boolean> receivedLocation = new Callable<Boolean>() {
+            @Override
+            public Boolean call() {
+                return mJavascriptStatusReceiver.mHasPosition;
+            }
+        };
+        PollingCheck.check("JS didn't get position", POLLING_TIMEOUT, receivedLocation);
+        chromeClientAcceptAlways.mReceivedRequest = false;
+        mJavascriptStatusReceiver.clearState();
+        // Load the same URL again
+        loadUrlAndUpdateLocation(URL_1);
+        PollingCheck.check("JS didn't get position", POLLING_TIMEOUT, receivedLocation);
+        // Assert prompt for geolocation permission is not called the second time
+        assertFalse(chromeClientAcceptAlways.mReceivedRequest);
+        // Check that the permission is in GeolocationPermissions
+        ValueCheck<Boolean> trueCheck = new ValueCheck<Boolean>(true);
+        GeolocationPermissions.getInstance().getAllowed(URL_1, trueCheck);
+        trueCheck.run();
+        Set<String> acceptedOrigins = new TreeSet<String>();
+        acceptedOrigins.add(URL_1);
+        ValueCheck<Set<String>> originCheck = new ValueCheck<Set<String>>(acceptedOrigins);
+        GeolocationPermissions.getInstance().getOrigins(originCheck);
+        originCheck.run();
+
+        // URL_2 should get a prompt
+        chromeClientAcceptAlways.mReceivedRequest = false;
+        loadUrlAndUpdateLocation(URL_2);
+        // Checking the callback for geolocation permission prompt is called
+        PollingCheck.check("Geolocation prompt not called", POLLING_TIMEOUT, receivedRequest);
+        PollingCheck.check("JS didn't get position", POLLING_TIMEOUT, receivedLocation);
+        acceptedOrigins.add(URL_2);
+        originCheck = new ValueCheck<Set<String>>(acceptedOrigins);
+        GeolocationPermissions.getInstance().getOrigins(originCheck);
+        originCheck.run();
+        // Remove a domain manually that was added by the callback
+        GeolocationPermissions.getInstance().clear(URL_1);
+        acceptedOrigins.remove(URL_1);
+        originCheck = new ValueCheck<Set<String>>(acceptedOrigins);
+        GeolocationPermissions.getInstance().getOrigins(originCheck);
+        originCheck.run();
+    }
+
+    // Test the GeolocationPermissions API
+    public void testGeolocationPermissions() {
+        Set<String> acceptedOrigins = new TreeSet<String>();
+        ValueCheck<Boolean> falseCheck = new ValueCheck<Boolean>(false);
+        GeolocationPermissions.getInstance().getAllowed(URL_2, falseCheck);
+        falseCheck.run();
+        ValueCheck<Set<String>> originCheck = new ValueCheck<Set<String>>(acceptedOrigins);
+        GeolocationPermissions.getInstance().getOrigins(originCheck);
+        originCheck.run();
+
+        // Remove a domain that has not been allowed
+        GeolocationPermissions.getInstance().clear(URL_2);
+        acceptedOrigins.remove(URL_2);
+        originCheck = new ValueCheck<Set<String>>(acceptedOrigins);
+        GeolocationPermissions.getInstance().getOrigins(originCheck);
+        originCheck.run();
+
+        // Add a domain
+        acceptedOrigins.add(URL_2);
+        GeolocationPermissions.getInstance().allow(URL_2);
+        originCheck = new ValueCheck<Set<String>>(acceptedOrigins);
+        GeolocationPermissions.getInstance().getOrigins(originCheck);
+        originCheck.run();
+        ValueCheck<Boolean> trueCheck = new ValueCheck<Boolean>(true);
+        GeolocationPermissions.getInstance().getAllowed(URL_2, trueCheck);
+        trueCheck.run();
+
+        // Add a domain
+        acceptedOrigins.add(URL_1);
+        GeolocationPermissions.getInstance().allow(URL_1);
+        originCheck = new ValueCheck<Set<String>>(acceptedOrigins);
+        GeolocationPermissions.getInstance().getOrigins(originCheck);
+        originCheck.run();
+
+        // Remove a domain that has been allowed
+        GeolocationPermissions.getInstance().clear(URL_2);
+        acceptedOrigins.remove(URL_2);
+        originCheck = new ValueCheck<Set<String>>(acceptedOrigins);
+        GeolocationPermissions.getInstance().getOrigins(originCheck);
+        originCheck.run();
+        falseCheck = new ValueCheck<Boolean>(false);
+        GeolocationPermissions.getInstance().getAllowed(URL_2, falseCheck);
+        falseCheck.run();
+
+        // Try to clear all domains
+        GeolocationPermissions.getInstance().clearAll();
+        acceptedOrigins.clear();
+        originCheck = new ValueCheck<Set<String>>(acceptedOrigins);
+        GeolocationPermissions.getInstance().getOrigins(originCheck);
+        originCheck.run();
+
+        // Add a domain
+        acceptedOrigins.add(URL_1);
+        GeolocationPermissions.getInstance().allow(URL_1);
+        originCheck = new ValueCheck<Set<String>>(acceptedOrigins);
+        GeolocationPermissions.getInstance().getOrigins(originCheck);
+        originCheck.run();
+    }
+
+    // Test loading pages and checks rejecting once and recjecting the domain forever
+    public void testSimpleGeolocationRequestReject() throws Exception {
+        final TestSimpleGeolocationRequestWebChromeClient chromeClientRejectOnce =
+                new TestSimpleGeolocationRequestWebChromeClient(mOnUiThread, false, false);
+        mOnUiThread.setWebChromeClient(chromeClientRejectOnce);
+        // Load url once, and the callback should accept the domain for all future loads
+        mOnUiThread.loadUrlAndWaitForCompletion(URL_1);
+        Callable<Boolean> receivedRequest = new Callable<Boolean>() {
+            @Override
+            public Boolean call() {
+                return chromeClientRejectOnce.mReceivedRequest;
+            }
+        };
+        PollingCheck.check("Geolocation prompt not called", POLLING_TIMEOUT, receivedRequest);
+        Callable<Boolean> locationDenied = new Callable<Boolean>() {
+            @Override
+            public Boolean call() {
+                return mJavascriptStatusReceiver.mDenied;
+            }
+        };
+        PollingCheck.check("JS didn't get position", POLLING_TIMEOUT, locationDenied);
+        // Same result should happen on next run
+        mOnUiThread.loadUrlAndWaitForCompletion(URL_1);
+        PollingCheck.check("Geolocation prompt not called", POLLING_TIMEOUT, receivedRequest);
+        PollingCheck.check("JS didn't get position", POLLING_TIMEOUT, locationDenied);
+
+        // Try to reject forever
+        final TestSimpleGeolocationRequestWebChromeClient chromeClientRejectAlways =
+            new TestSimpleGeolocationRequestWebChromeClient(mOnUiThread, false, true);
+        mOnUiThread.setWebChromeClient(chromeClientRejectAlways);
+        mOnUiThread.loadUrlAndWaitForCompletion(URL_2);
+        PollingCheck.check("Geolocation prompt not called", POLLING_TIMEOUT, receivedRequest);
+        PollingCheck.check("JS didn't get position", POLLING_TIMEOUT, locationDenied);
+        // second load should now not get a prompt
+        chromeClientRejectAlways.mReceivedRequest = false;
+        mOnUiThread.loadUrlAndWaitForCompletion(URL_2);
+        PollingCheck.check("JS didn't get position", POLLING_TIMEOUT, locationDenied);
+        PollingCheck.check("Geolocation prompt not called", POLLING_TIMEOUT, receivedRequest);
+
+        // Test if it gets added to origins
+        Set<String> acceptedOrigins = new TreeSet<String>();
+        acceptedOrigins.add(URL_2);
+        ValueCheck<Set<String>> domainCheck = new ValueCheck<Set<String>>(acceptedOrigins);
+        GeolocationPermissions.getInstance().getOrigins(domainCheck);
+        domainCheck.run();
+        // And now check that getAllowed returns false
+        ValueCheck<Boolean> falseCheck = new ValueCheck<Boolean>(false);
+        GeolocationPermissions.getInstance().getAllowed(URL_1, falseCheck);
+        falseCheck.run();
+    }
+
+    // Object added to the page via AddJavascriptInterface() that is used by the test Javascript to
+    // notify back to Java when a location or error is received.
+    public final static class JavascriptStatusReceiver {
+        public volatile boolean mHasPosition = false;
+        public volatile boolean mDenied = false;
+        public volatile boolean mUnavailable = false;
+        public volatile boolean mTimeout = false;
+
+        public void clearState() {
+            mHasPosition = false;
+            mDenied = false;
+            mUnavailable = false;
+            mTimeout = false;
+        }
+
+        @JavascriptInterface
+        public void errorDenied() {
+            mDenied = true;
+        }
+
+        @JavascriptInterface
+        public void errorUnavailable() {
+            mUnavailable = true;
+        }
+
+        @JavascriptInterface
+        public void errorTimeout() {
+            mTimeout = true;
+        }
+
+        @JavascriptInterface
+        public void gotLocation() {
+            mHasPosition = true;
+        }
+    }
+}
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebHistoryItemTest.java b/tests/tests/webkit/src/android/webkit/cts/WebHistoryItemTest.java
index 4f2113b..2eb81fc 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebHistoryItemTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebHistoryItemTest.java
@@ -55,7 +55,6 @@
         assertEquals(1, list.getSize());
         WebHistoryItem item = list.getCurrentItem();
         assertNotNull(item);
-        int firstId = item.getId();
         assertEquals(url, item.getUrl());
         assertEquals(url, item.getOriginalUrl());
         assertEquals(TestHtmlConstants.HELLO_WORLD_TITLE, item.getTitle());
@@ -69,7 +68,5 @@
         item = list.getCurrentItem();
         assertNotNull(item);
         assertEquals(TestHtmlConstants.BR_TAG_TITLE, item.getTitle());
-        int secondId = item.getId();
-        assertTrue(firstId != secondId);
     }
 }
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
index 054217e..73a2d40 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
@@ -155,47 +155,6 @@
         assertEquals(customUserAgent, mOnUiThread.getTitle());
     }
 
-    @SuppressWarnings("deprecation")
-    public void testAccessUserAgent() throws Exception {
-        startWebServer();
-        String url = mWebServer.getUserAgentUrl();
-
-        mSettings.setUserAgent(1);
-        assertEquals(1, mSettings.getUserAgent());
-        mOnUiThread.loadUrlAndWaitForCompletion(url);
-        String userAgent1 = mOnUiThread.getTitle();
-        assertNotNull(userAgent1);
-
-        mSettings.setUserAgent(3);
-        assertEquals(1, mSettings.getUserAgent());
-        mOnUiThread.loadUrlAndWaitForCompletion(url);
-        assertEquals(userAgent1, mOnUiThread.getTitle());
-
-        mSettings.setUserAgent(2);
-        assertEquals(2, mSettings.getUserAgent());
-        mOnUiThread.loadUrlAndWaitForCompletion(url);
-        String userAgent2 = mOnUiThread.getTitle();
-        assertNotNull(userAgent2);
-
-        mSettings.setUserAgent(3);
-        assertEquals(2, mSettings.getUserAgent());
-        mOnUiThread.loadUrlAndWaitForCompletion(url);
-        assertEquals(userAgent2, mOnUiThread.getTitle());
-
-        mSettings.setUserAgent(0);
-        assertEquals(0, mSettings.getUserAgent());
-        mOnUiThread.loadUrlAndWaitForCompletion(url);
-        String userAgent0 = mOnUiThread.getTitle();
-        assertNotNull(userAgent0);
-
-        final String customUserAgent = "Cts/Test";
-        mSettings.setUserAgentString(customUserAgent);
-        assertEquals(-1, mSettings.getUserAgent());
-        mOnUiThread.loadUrlAndWaitForCompletion(url);
-        assertEquals(customUserAgent, mOnUiThread.getTitle());
-    }
-
-
     public void testAccessAllowFileAccess() {
         // This test is not compatible with 4.0.3
         if ("4.0.3".equals(Build.VERSION.RELEASE)) {
@@ -387,11 +346,9 @@
         new PollingCheck(WEBVIEW_TIMEOUT) {
             @Override
             protected boolean check() {
-                String title = mOnUiThread.getTitle();
-                return title != null && title.length() > 0;
+                return "Popup blocked".equals(mOnUiThread.getTitle());
             }
         }.run();
-        assertEquals("Popup blocked", mOnUiThread.getTitle());
 
         mSettings.setJavaScriptCanOpenWindowsAutomatically(true);
         assertTrue(mSettings.getJavaScriptCanOpenWindowsAutomatically());
@@ -399,15 +356,9 @@
         new PollingCheck(WEBVIEW_TIMEOUT) {
             @Override
             protected boolean check() {
-                String title = mOnUiThread.getTitle();
-                // The title may not change immediately after loading, so
-                // we have to discount the initial "Popup blocked" from the
-                // previous load.
-                return title != null && title.length() > 0
-                        && !title.equals("Popup blocked");
+                return "Popup allowed".equals(mOnUiThread.getTitle());
             }
         }.run();
-        assertEquals("Popup allowed", mOnUiThread.getTitle());
     }
 
     public void testAccessJavaScriptEnabled() throws Exception {
@@ -417,10 +368,9 @@
         new PollingCheck(WEBVIEW_TIMEOUT) {
             @Override
             protected boolean check() {
-                return mOnUiThread.getTitle() != null;
+                return "javascript on".equals(mOnUiThread.getTitle());
             }
         }.run();
-        assertEquals("javascript on", mOnUiThread.getTitle());
 
         mSettings.setJavaScriptEnabled(false);
         assertFalse(mSettings.getJavaScriptEnabled());
@@ -428,10 +378,10 @@
         new PollingCheck(WEBVIEW_TIMEOUT) {
             @Override
             protected boolean check() {
-                return mOnUiThread.getTitle() != null;
+                return "javascript off".equals(mOnUiThread.getTitle());
             }
         }.run();
-        assertEquals("javascript off", mOnUiThread.getTitle());
+
     }
 
     public void testAccessLayoutAlgorithm() {
@@ -444,13 +394,6 @@
         assertEquals(WebSettings.LayoutAlgorithm.SINGLE_COLUMN, mSettings.getLayoutAlgorithm());
     }
 
-    public void testAccessLightTouchEnabled() {
-        assertFalse(mSettings.getLightTouchEnabled());
-
-        mSettings.setLightTouchEnabled(true);
-        assertTrue(mSettings.getLightTouchEnabled());
-    }
-
     public void testAccessMinimumFontSize() {
         assertEquals(8, mSettings.getMinimumFontSize());
 
@@ -477,13 +420,6 @@
         assertEquals(10, mSettings.getMinimumLogicalFontSize());
     }
 
-    public void testAccessNavDump() {
-        assertFalse(mSettings.getNavDump());
-
-        mSettings.setNavDump(true);
-        assertTrue(mSettings.getNavDump());
-    }
-
     public void testAccessPluginsEnabled() {
         assertFalse(mSettings.getPluginsEnabled());
 
@@ -507,13 +443,6 @@
         assertFalse(mSettings.getSaveFormData());
     }
 
-    public void testAccessSavePassword() {
-        assertTrue(mSettings.getSavePassword());
-
-        mSettings.setSavePassword(false);
-        assertFalse(mSettings.getSavePassword());
-    }
-
     public void testAccessTextSize() {
         assertEquals(TextSize.NORMAL, mSettings.getTextSize());
 
@@ -727,7 +656,7 @@
         mOnUiThread.clearCache(true);
         mOnUiThread.loadUrlAndWaitForCompletion(
             mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL));
-        assertEquals(TestHtmlConstants.WEBPAGE_NOT_AVAILABLE_TITLE, mOnUiThread.getTitle());
+        assertFalse(TestHtmlConstants.HELLO_WORLD_TITLE.equals(mOnUiThread.getTitle()));
         mOnUiThread.loadDataAndWaitForCompletion(getNetworkImageHtml(), "text/html", null);
         assertEquals(EMPTY_IMAGE_HEIGHT, mOnUiThread.getTitle());
         mOnUiThread.loadDataAndWaitForCompletion(DATA_URL_IMAGE_HTML, "text/html", null);
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
index 6c66f52..46ad087 100755
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
@@ -33,6 +33,8 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.StrictMode;
+import android.os.StrictMode.ThreadPolicy;
 import android.os.SystemClock;
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.UiThreadTest;
@@ -41,8 +43,6 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
-import android.webkit.CacheManager;
-import android.webkit.CacheManager.CacheResult;
 import android.webkit.ConsoleMessage;
 import android.webkit.DownloadListener;
 import android.webkit.JavascriptInterface;
@@ -124,6 +124,18 @@
         mWebServer = new CtsTestServer(getActivity(), secure);
     }
 
+    private void stopWebServer() throws Exception {
+        assertNotNull(mWebServer);
+        ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
+        ThreadPolicy tmpPolicy = new ThreadPolicy.Builder(oldPolicy)
+                .permitNetwork()
+                .build();
+        StrictMode.setThreadPolicy(tmpPolicy);
+        mWebServer.shutdown();
+        mWebServer = null;
+        StrictMode.setThreadPolicy(oldPolicy);
+    }
+
     @UiThreadTest
     public void testConstructor() {
         new WebView(getActivity());
@@ -138,23 +150,11 @@
          * http://en.wikipedia.org/wiki/Postal_address#United_States
          * http://www.usps.com/
          */
-        // full address, invalid zip code
-        assertNull(WebView.findAddress("455 LARKSPUR DRIVE CALIFORNIA SPRINGS CALIFORNIA 92926"));
         // full address
         assertEquals("455 LARKSPUR DRIVE CALIFORNIA SPRINGS CALIFORNIA 92826",
                 WebView.findAddress("455 LARKSPUR DRIVE CALIFORNIA SPRINGS CALIFORNIA 92826"));
-        // full address ( with abbreviated street type and state)
-        assertEquals("455 LARKSPUR DR CALIFORNIA SPRINGS CA 92826",
-                WebView.findAddress("455 LARKSPUR DR CALIFORNIA SPRINGS CA 92826"));
-        // misspell the state ( CALIFORNIA -> CALIFONIA )
-        assertNull(WebView.findAddress("455 LARKSPUR DRIVE CALIFORNIA SPRINGS CALIFONIA 92826"));
-        // without optional zip code
-        assertEquals("455 LARKSPUR DR CALIFORNIA SPRINGS CA",
-                WebView.findAddress("455 LARKSPUR DR CALIFORNIA SPRINGS CA"));
-        // house number, street name and street type are missing
-        assertNull(WebView.findAddress("CALIFORNIA SPRINGS CA"));
-        // city & state are missing
-        assertNull(WebView.findAddress("455 LARKSPUR DR"));
+        // not an address
+        assertNull(WebView.findAddress("This is not an address: no town, no state, no zip."));
     }
 
     @SuppressWarnings("deprecation")
@@ -619,66 +619,6 @@
         }.run();
     }
 
-    public void testSaveAndRestorePicture() throws Throwable {
-        mWebView.setBackgroundColor(Color.CYAN);
-        startWebServer(false);
-        final String url = mWebServer.getAssetUrl(TestHtmlConstants.BLANK_PAGE_URL);
-        mOnUiThread.loadUrlAndWaitForCompletion(url);
-        getInstrumentation().waitForIdleSync();
-
-        final Bundle bundle = new Bundle();
-        final File f = getActivity().getFileStreamPath("snapshot");
-        if (f.exists()) {
-            f.delete();
-        }
-
-        try {
-            assertTrue(bundle.isEmpty());
-            assertEquals(0, f.length());
-            assertTrue(mOnUiThread.savePicture(bundle, f));
-
-            // File saving is done in a separate thread.
-            new PollingCheck() {
-                @Override
-                protected boolean check() {
-                    return f.length() > 0;
-                }
-            }.run();
-
-            assertFalse(bundle.isEmpty());
-
-            Picture p = Picture.createFromStream(new FileInputStream(f));
-            Bitmap b = Bitmap.createBitmap(p.getWidth(), p.getHeight(), Config.ARGB_8888);
-            p.draw(new Canvas(b));
-            assertBitmapFillWithColor(b, Color.CYAN);
-
-            mOnUiThread.setBackgroundColor(Color.WHITE);
-            mOnUiThread.reloadAndWaitForCompletion();
-            getInstrumentation().waitForIdleSync();
-
-            runTestOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    Bitmap b = Bitmap.createBitmap(mWebView.getWidth(), mWebView.getHeight(),
-                            Config.ARGB_8888);
-                    mWebView.draw(new Canvas(b));
-                    assertBitmapFillWithColor(b, Color.WHITE);
-
-                    // restorePicture is only supported in software rendering
-                    mWebView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
-                    assertTrue(mWebView.restorePicture(bundle, f));
-                }
-            });
-            getInstrumentation().waitForIdleSync();
-            // Cannot test whether the picture has been restored successfully.
-            // Drawing the webview into a canvas will draw white, but on the display it is cyan
-        } finally {
-            if (f.exists()) {
-                f.delete();
-            }
-        }
-    }
-
     @UiThreadTest
     public void testAccessHttpAuthUsernamePassword() {
         try {
@@ -741,28 +681,10 @@
         }
     }
 
-    @UiThreadTest
-    public void testSavePassword() {
-        WebViewDatabase db = WebViewDatabase.getInstance(getActivity());
-        try {
-            db.clearUsernamePassword();
-
-            String host = "http://localhost:8080";
-            String userName = "user";
-            String password = "password";
-            assertFalse(db.hasUsernamePassword());
-            mWebView.savePassword(host, userName, password);
-            assertTrue(db.hasUsernamePassword());
-        } finally {
-            db.clearUsernamePassword();
-        }
-    }
-
     public void testLoadData() throws Throwable {
         final String HTML_CONTENT =
                 "<html><head><title>Hello,World!</title></head><body></body>" +
                 "</html>";
-        assertNull(mOnUiThread.getTitle());
         mOnUiThread.loadDataAndWaitForCompletion(HTML_CONTENT,
                 "text/html", null);
         assertEquals("Hello,World!", mOnUiThread.getTitle());
@@ -788,7 +710,6 @@
 
     @UiThreadTest
     public void testLoadDataWithBaseUrl() throws Throwable {
-        assertNull(mWebView.getTitle());
         assertNull(mWebView.getUrl());
         String imgUrl = TestHtmlConstants.SMALL_IMG_URL; // relative
 
@@ -1024,40 +945,6 @@
     }
 
     @UiThreadTest
-    public void testClearCache() throws Exception {
-        final File cacheFileBaseDir = CacheManager.getCacheFileBaseDir();
-        mWebView.clearCache(true);
-        assertEquals(0, cacheFileBaseDir.list().length);
-
-        startWebServer(false);
-        final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
-        mOnUiThread.loadUrlAndWaitForCompletion(url);
-        new PollingCheck(TEST_TIMEOUT) {
-            @Override
-            protected boolean check() {
-                CacheResult result = CacheManager.getCacheFile(url, null);
-                return result != null;
-            }
-        }.run();
-        int cacheFileCount = cacheFileBaseDir.list().length;
-        assertTrue(cacheFileCount > 0);
-
-        mWebView.clearCache(false);
-        // the cache files are still there
-        // can not check other effects of the method
-        assertEquals(cacheFileCount, cacheFileBaseDir.list().length);
-
-        mWebView.clearCache(true);
-        // check the files are deleted
-        new PollingCheck(TEST_TIMEOUT) {
-            @Override
-            protected boolean check() {
-                return cacheFileBaseDir.list().length == 0;
-            }
-        }.run();
-    }
-
-    @UiThreadTest
     public void testPlatformNotifications() {
         WebView.enablePlatformNotifications();
         WebView.disablePlatformNotifications();
@@ -1475,21 +1362,30 @@
     }
 
     @UiThreadTest
-    public void testSetAndGetCertificate() {
-        assertNull(mWebView.getCertificate());
-        SslCertificate certificate = new SslCertificate("foo", "bar", new Date(42), new Date(43));
-        mWebView.setCertificate(certificate);
-        assertEquals(certificate, mWebView.getCertificate());
-    }
-
-    @UiThreadTest
     public void testInsecureSiteClearsCertificate() throws Throwable {
-        final SslCertificate certificate =
-                new SslCertificate("foo", "bar", new Date(42), new Date(43));
+        final class MockWebViewClient extends WaitForLoadedClient {
+            public MockWebViewClient() {
+                super(mOnUiThread);
+            }
+            @Override
+            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
+                handler.proceed();
+            }
+        }
+
+        startWebServer(true);
+        mOnUiThread.setWebViewClient(new MockWebViewClient());
+        mOnUiThread.loadUrlAndWaitForCompletion(
+                mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL));
+        SslCertificate cert = mWebView.getCertificate();
+        assertNotNull(cert);
+        assertEquals("Android", cert.getIssuedTo().getUName());
+
+        stopWebServer();
+
         startWebServer(false);
-        final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
-        mWebView.setCertificate(certificate);
-        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        mOnUiThread.loadUrlAndWaitForCompletion(
+                mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL));
         assertNull(mWebView.getCertificate());
     }
 
@@ -1505,11 +1401,17 @@
             }
         }
 
+        startWebServer(false);
+        mOnUiThread.loadUrlAndWaitForCompletion(
+                mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL));
+        assertNull(mWebView.getCertificate());
+
+        stopWebServer();
+
         startWebServer(true);
-        final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
         mOnUiThread.setWebViewClient(new MockWebViewClient());
-        mWebView.setCertificate(null);
-        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        mOnUiThread.loadUrlAndWaitForCompletion(
+                mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL));
         SslCertificate cert = mWebView.getCertificate();
         assertNotNull(cert);
         assertEquals("Android", cert.getIssuedTo().getUName());
@@ -1685,15 +1587,13 @@
         // WebViewClient.shouldOverrideUrlLoading() returns false, so
         // the WebView will load the new URL.
         mOnUiThread.setDownloadListener(listener);
+        mWebView.getSettings().setJavaScriptEnabled(true);
         mOnUiThread.loadDataAndWaitForCompletion(
-                "<html><body><a href=\"" + url
-                + "\">link</a></body></html>",
+                "<html><body onload=\"window.location = \'" + url + "\'\"></body></html>",
                 "text/html", null);
         // Wait for layout to complete before setting focus.
         getInstrumentation().waitForIdleSync();
-        assertTrue(mOnUiThread.requestFocus(View.FOCUS_DOWN, null));
-        getInstrumentation().waitForIdleSync();
-        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_CENTER);
+
         new PollingCheck(TEST_TIMEOUT) {
             @Override
             protected boolean check() {
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebView_WebViewTransportTest.java b/tests/tests/webkit/src/android/webkit/cts/WebView_WebViewTransportTest.java
index eaa5e69..dd784be 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebView_WebViewTransportTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebView_WebViewTransportTest.java
@@ -16,16 +16,22 @@
 
 package android.webkit.cts;
 
-import android.test.AndroidTestCase;
+import android.test.ActivityInstrumentationTestCase2;
 import android.test.UiThreadTest;
 import android.webkit.WebView;
 import android.webkit.WebView.WebViewTransport;
 
 
-public class WebView_WebViewTransportTest extends AndroidTestCase {
+public class WebView_WebViewTransportTest
+        extends ActivityInstrumentationTestCase2<WebViewStubActivity> {
+
+    public WebView_WebViewTransportTest() {
+        super("com.android.cts.stub", WebViewStubActivity.class);
+    }
+
     @UiThreadTest
     public void testAccessWebView() {
-        WebView webView = new WebView(mContext);
+        WebView webView = getActivity().getWebView();
         WebViewTransport transport = webView.new WebViewTransport();
 
         assertNull(transport.getWebView());
diff --git a/tests/tests/widget/src/android/widget/cts/GridLayoutTest.java b/tests/tests/widget/src/android/widget/cts/GridLayoutTest.java
new file mode 100644
index 0000000..638fe96
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/GridLayoutTest.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget.cts;
+
+import android.content.Context;
+import android.test.ActivityInstrumentationTestCase;
+import android.util.AttributeSet;
+import android.util.Xml;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsoluteLayout;
+import android.widget.Button;
+import android.widget.GridLayout;
+import android.widget.TextView;
+import com.android.cts.stub.R;
+import org.xmlpull.v1.XmlPullParser;
+
+import static android.view.ViewGroup.LAYOUT_MODE_OPTICAL_BOUNDS;
+import static android.widget.GridLayout.spec;
+
+/**
+ * Test {@link android.widget.GridLayout}.
+ */
+public class GridLayoutTest extends ActivityInstrumentationTestCase<GridLayoutStubActivity> {
+
+    // The size of the off-screen test container in which we we will testing layout.
+    public static final int MAX_X = 2000;
+    public static final int MAX_Y = 2000;
+
+    private static abstract class Alignment {
+        String name;
+        int gravity;
+
+        abstract int getValue(View v);
+
+        protected Alignment(String name, int gravity) {
+            this.name = name;
+            this.gravity = gravity;
+        }
+    }
+
+    private static final Alignment[] HORIZONTAL_ALIGNMENTS = {
+            new Alignment("LEFT", Gravity.LEFT) {
+                @Override
+                int getValue(View v) {
+                    return v.getLeft();
+                }
+            },
+            new Alignment("CENTER", Gravity.CENTER_HORIZONTAL) {
+                @Override
+                int getValue(View v) {
+                    return (v.getLeft() + v.getRight()) / 2;
+                }
+            },
+            new Alignment("RIGHT", Gravity.RIGHT) {
+                @Override
+                int getValue(View v) {
+                    return v.getRight();
+                }
+            },
+            new Alignment("FILL", Gravity.FILL_HORIZONTAL) {
+                @Override
+                int getValue(View v) {
+                    return v.getWidth();
+                }
+            }
+    };
+
+    private static final Alignment[] VERTICAL_ALIGNMENTS = {
+            new Alignment("TOP", Gravity.TOP) {
+                @Override
+                int getValue(View v) {
+                    return v.getTop();
+                }
+            },
+            new Alignment("CENTER", Gravity.CENTER_VERTICAL) {
+                @Override
+                int getValue(View v) {
+                    return (v.getTop() + v.getBottom()) / 2;
+                }
+            },
+            new Alignment("BASELINE", Gravity.NO_GRAVITY) {
+                @Override
+                int getValue(View v) {
+                    return v.getTop() + v.getBaseline();
+                }
+            },
+            new Alignment("BOTTOM", Gravity.BOTTOM) {
+                @Override
+                int getValue(View v) {
+                    return v.getBottom();
+                }
+            },
+            new Alignment("FILL", Gravity.FILL_VERTICAL) {
+                @Override
+                int getValue(View v) {
+                    return v.getHeight();
+                }
+            }
+    };
+
+    private Context mContext;
+
+    public GridLayoutTest() {
+        super("com.android.cts.stub", GridLayoutStubActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mContext = getInstrumentation().getTargetContext();
+    }
+
+    public void testConstructor() {
+        new GridLayout(mContext);
+
+        new GridLayout(mContext, null);
+
+        XmlPullParser parser = mContext.getResources().getXml(R.layout.gridlayout_layout);
+        AttributeSet attrs = Xml.asAttributeSet(parser);
+        new GridLayout(mContext, attrs);
+
+        try {
+            new GridLayout(null, null);
+            fail("should throw NullPointerException.");
+        } catch (NullPointerException e) {
+        }
+    }
+
+    public void testCheckLayoutParams() {
+        GridLayout gridLayout = new GridLayout(mContext);
+
+        gridLayout.addView(new TextView(mContext), new AbsoluteLayout.LayoutParams(0, 0, 0, 0));
+
+        gridLayout.addView(new TextView(mContext), new GridLayout.LayoutParams(
+                GridLayout.spec(0),
+                GridLayout.spec(0)));
+
+    }
+
+    public void testGenerateDefaultLayoutParams() {
+        GridLayout gridLayout = new GridLayout(mContext);
+        ViewGroup.LayoutParams lp = gridLayout.generateLayoutParams(null);
+        assertNotNull(lp);
+        assertTrue(lp instanceof GridLayout.LayoutParams);
+        assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, lp.width);
+        assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, lp.height);
+    }
+
+    private View[][] populate(GridLayout container) {
+        Context context = container.getContext();
+        int N = VERTICAL_ALIGNMENTS.length;
+        int M = HORIZONTAL_ALIGNMENTS.length;
+
+        View[][] table = new View[N + 1][M + 1];
+
+        {
+            TextView v = new TextView(context);
+            GridLayout.LayoutParams lp = new GridLayout.LayoutParams(spec(0), spec(0));
+            lp.setGravity(Gravity.CENTER);
+            v.setText("*");
+            container.addView(v, lp);
+        }
+        for (int i = 0; i < N; i++) {
+            Alignment va = VERTICAL_ALIGNMENTS[i];
+            int row = i + 1;
+            GridLayout.LayoutParams lp = new GridLayout.LayoutParams(spec(row), spec(0));
+            lp.setGravity(va.gravity | Gravity.CENTER_HORIZONTAL);
+            TextView v = new TextView(context);
+            v.setGravity(Gravity.CENTER);
+            v.setText(va.name);
+            container.addView(v, lp);
+            table[row][0] = v;
+        }
+        for (int j = 0; j < M; j++) {
+            Alignment ha = HORIZONTAL_ALIGNMENTS[j];
+            int col = j + 1;
+            GridLayout.LayoutParams lp = new GridLayout.LayoutParams(spec(0), spec(col));
+            lp.setGravity(Gravity.CENTER_VERTICAL | ha.gravity);
+            TextView v = new TextView(context);
+            v.setGravity(Gravity.CENTER);
+            v.setText(ha.name);
+            container.addView(v, lp);
+            table[0][col] = v;
+        }
+        for (int i = 0; i < N; i++) {
+            for (int j = 0; j < M; j++) {
+                Alignment ha = HORIZONTAL_ALIGNMENTS[j];
+                Alignment va = VERTICAL_ALIGNMENTS[i];
+                int row = i + 1;
+                int col = j + 1;
+                GridLayout.LayoutParams lp = new GridLayout.LayoutParams(spec(row), spec(col));
+                lp.setGravity(va.gravity | ha.gravity);
+                TextView v = new Button(context);
+                v.setText("X");
+                v.setTextSize(10 + 5 * row * col);
+                container.addView(v, lp);
+                table[row][col] = v;
+            }
+        }
+        return table;
+    }
+
+    private void testAlignment(int row, int col, Alignment a, View v0, View v1, String group) {
+        int a0 = a.getValue(v0);
+        int a1 = a.getValue(v1);
+        assertEquals("View at row " + row + ", column " + col + " was not " + a.name +
+                " aligned with its title " + group + ";",
+                a0, a1);
+    }
+
+    private void test(GridLayout p, View[][] table) {
+        p.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+        p.layout(0, 0, MAX_X, MAX_Y);
+
+        int N = VERTICAL_ALIGNMENTS.length;
+        int M = HORIZONTAL_ALIGNMENTS.length;
+
+        // test all horizontal alignments in each column
+        for (int j = 0; j < M; j++) {
+            int col = j + 1;
+            View v0 = table[0][col];
+            Alignment alignment = HORIZONTAL_ALIGNMENTS[j];
+            for (int i = 0; i < N; i++) {
+                int row = i + 1;
+                testAlignment(row, col, alignment, v0, table[row][col], "column");
+            }
+        }
+
+        // test all vertical alignments in each row
+        for (int i = 0; i < N; i++) {
+            int row = i + 1;
+            View v0 = table[row][0];
+            Alignment alignment = VERTICAL_ALIGNMENTS[i];
+            for (int j = 0; j < M; j++) {
+                int col = j + 1;
+                testAlignment(row, col, alignment, v0, table[row][col], "row");
+            }
+        }
+    }
+    public void testAlignment() {
+        GridLayout p = new GridLayout(mContext);
+        View[][] table = populate(p);
+        test(p, table);
+        //p.setLayoutMode(ViewGroup.LAYOUT_MODE_OPTICAL_BOUNDS);
+        //test(p, table);
+    }
+}
diff --git a/tests/tests/widget/src/android/widget/cts/ViewFlipperTest.java b/tests/tests/widget/src/android/widget/cts/ViewFlipperTest.java
index bcb417a..f223466 100644
--- a/tests/tests/widget/src/android/widget/cts/ViewFlipperTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ViewFlipperTest.java
@@ -93,6 +93,7 @@
 
         // wait for a longer time to make sure the view flipping is completed.
         waitForViewFlipping(FLIP_INTERVAL + 200);
+        getInstrumentation().waitForIdleSync();
         runTestOnUiThread(new Runnable() {
             public void run() {
                 ViewFlipper viewFlipper =
@@ -108,6 +109,7 @@
         });
 
         waitForViewFlipping(FLIP_INTERVAL + 200);
+        getInstrumentation().waitForIdleSync();
         runTestOnUiThread(new Runnable() {
             public void run() {
                 ViewFlipper viewFlipper =
diff --git a/tests/uiautomator/src/com/android/cts/uiautomatortest/CtsUiAutomatorTest.java b/tests/uiautomator/src/com/android/cts/uiautomatortest/CtsUiAutomatorTest.java
index 5de9805..73bab27 100644
--- a/tests/uiautomator/src/com/android/cts/uiautomatortest/CtsUiAutomatorTest.java
+++ b/tests/uiautomator/src/com/android/cts/uiautomatortest/CtsUiAutomatorTest.java
@@ -343,6 +343,7 @@
 
         Rect tb = textView.getBounds();
         UiDevice.getInstance().swipe(tb.right - 20, tb.centerY(), tb.left + 20, tb.centerY(), 50);
+        SystemClock.sleep(100);
         assertTrue("UiDevice swipe", "[ 2 ]".equals(textView.getText()));
     }
 
diff --git a/suite/pts/tools/Android.mk b/tests/uiautomator/test-apps/Android.mk
similarity index 98%
rename from suite/pts/tools/Android.mk
rename to tests/uiautomator/test-apps/Android.mk
index c141484..e790e1e 100644
--- a/suite/pts/tools/Android.mk
+++ b/tests/uiautomator/test-apps/Android.mk
@@ -1,4 +1,3 @@
-#
 # Copyright (C) 2012 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -12,6 +11,5 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-#
 
 include $(call all-subdir-makefiles)
diff --git a/tests/uiautomator/test-apps/CtsUiAutomatorApp/Android.mk b/tests/uiautomator/test-apps/CtsUiAutomatorApp/Android.mk
index e30816a..fe4342a 100644
--- a/tests/uiautomator/test-apps/CtsUiAutomatorApp/Android.mk
+++ b/tests/uiautomator/test-apps/CtsUiAutomatorApp/Android.mk
@@ -1,4 +1,3 @@
-#
 # Copyright (C) 2012 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -12,7 +11,6 @@
 # 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.
-#
 
 LOCAL_PATH:= $(call my-dir)
 
@@ -31,4 +29,3 @@
 LOCAL_PROGUARD_ENABLED := disabled
 
 include $(BUILD_PACKAGE)
-
diff --git a/tests/uiautomator/test-apps/CtsUiAutomatorApp/AndroidManifest.xml b/tests/uiautomator/test-apps/CtsUiAutomatorApp/AndroidManifest.xml
index 9b3d8b3..87c2d82 100644
--- a/tests/uiautomator/test-apps/CtsUiAutomatorApp/AndroidManifest.xml
+++ b/tests/uiautomator/test-apps/CtsUiAutomatorApp/AndroidManifest.xml
@@ -1,5 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-
 <!--
  * Copyright (C) 2011 The Android Open Source Project
  *
@@ -44,6 +43,6 @@
                 android:name="android.support.PARENT_ACTIVITY"
                 android:value="FragmentActivity" />
         </activity>
-        </application>
+     </application>
 
 </manifest>
diff --git a/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/layout/activity_main.xml b/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/layout/activity_main.xml
index f0e3bc9..72b68f3 100644
--- a/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/layout/activity_main.xml
+++ b/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/layout/activity_main.xml
@@ -28,4 +28,4 @@
         android:layout_centerVertical="true"
         android:text="@string/hello_world" />
 
-</RelativeLayout>
\ No newline at end of file
+</RelativeLayout>
diff --git a/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/menu/activity_main.xml b/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/menu/activity_main.xml
index 73c4db8..c54c287 100644
--- a/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/menu/activity_main.xml
+++ b/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/menu/activity_main.xml
@@ -23,4 +23,4 @@
         android:showAsAction="never"
         android:title="@string/menu_settings"/>
 
-</menu>
\ No newline at end of file
+</menu>
diff --git a/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/values/strings.xml b/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/values/strings.xml
index 78b829f..db2450a 100644
--- a/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/values/strings.xml
+++ b/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/values/strings.xml
@@ -1,5 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-
 <!--
  * Copyright (C) 2011 The Android Open Source Project
  *
diff --git a/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/values/styles.xml b/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/values/styles.xml
index 119c259..bce88ae 100644
--- a/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/values/styles.xml
+++ b/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/values/styles.xml
@@ -19,4 +19,4 @@
 
     <style name="AppTheme" parent="android:Theme.Light" />
 
-</resources>
\ No newline at end of file
+</resources>
diff --git a/tests/uiautomator/test-apps/CtsUiAutomatorApp/src/com/android/cts/uiautomator/MainActivity.java b/tests/uiautomator/test-apps/CtsUiAutomatorApp/src/com/android/cts/uiautomator/MainActivity.java
index 8d4f144..81833b7 100644
--- a/tests/uiautomator/test-apps/CtsUiAutomatorApp/src/com/android/cts/uiautomator/MainActivity.java
+++ b/tests/uiautomator/test-apps/CtsUiAutomatorApp/src/com/android/cts/uiautomator/MainActivity.java
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package com.android.cts.uiautomator;
 
 import android.content.Intent;
diff --git a/tools/cts-java-scanner/src/com/android/cts/javascanner/DocletRunner.java b/tools/cts-java-scanner/src/com/android/cts/javascanner/DocletRunner.java
index ba87156..25a4ec1 100644
--- a/tools/cts-java-scanner/src/com/android/cts/javascanner/DocletRunner.java
+++ b/tools/cts-java-scanner/src/com/android/cts/javascanner/DocletRunner.java
@@ -72,6 +72,7 @@
         // PTS adds PtsAndroidTestCase
         sourcePath.add("./cts/suite/pts/deviceTests/ptsutil/src");
         sourcePath.add("./cts/libs/util/src");
+        sourcePath.add("./frameworks/testing/uiautomator/library/src");
         sourcePath.add(sourceDir.toString());
         return join(sourcePath, ":");
     }
diff --git a/tools/cts-native-scanner/Android.mk b/tools/cts-native-scanner/Android.mk
index f8b1629..8bcff1c 100644
--- a/tools/cts-native-scanner/Android.mk
+++ b/tools/cts-native-scanner/Android.mk
@@ -34,10 +34,5 @@
 	$(copy-file-to-new-target)
 	$(hide) chmod 755 $@
 
-# the other stuff
-# ============================================================
-subdirs := $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \
-		src \
-	))
-
-include $(subdirs)
+# Build all sub-directories
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tools/cts-native-scanner/src/com/android/cts/nativescanner/TestScanner.java b/tools/cts-native-scanner/src/com/android/cts/nativescanner/TestScanner.java
index 9411566..0a1b207 100644
--- a/tools/cts-native-scanner/src/com/android/cts/nativescanner/TestScanner.java
+++ b/tools/cts-native-scanner/src/com/android/cts/nativescanner/TestScanner.java
@@ -30,12 +30,14 @@
  * Scanner of C++ gTest source files.
  *
  * It looks for test declarations and outputs a file following this format:
- *
- * class:TestClass1
- * method:testMethod1
- * method:testMethod2
- * class:TestClass2
- * method:testMethod1
+ * suite:TestSuite
+ * case:TestCase1
+ * test:Test1
+ * test:Test2
+ * suite:TestSuite
+ * case:TestCase2
+ * test:Test1
+ * test:Test2
  *
  */
 class TestScanner {
@@ -71,38 +73,36 @@
             if (file.isDirectory()) {
                 scanDir(file, testNames);
             } else {
-                scanFile(file, testNames);
+                scanFile(new Scanner(file), testNames);
             }
         }
     }
-    // We want to find lines like class SLObjectCreationTest : public ::testing::Test { ...
-    // and extract the "SLObjectCreationTest" as group #1
-    private static final Pattern CLASS_REGEX =
-            Pattern.compile("\\s*class\\s+(\\w+).*");
 
     // We want to find lines like TEST_F(SLObjectCreationTest, testAudioPlayerFromFdCreation) { ...
-    // and extract the "testAudioPlayerFromFdCreation" as group #1
+    // and extract the "SLObjectCreationTest" as group #1,
+    // "testAudioPlayerFromFdCreation" as group #2
     private static final Pattern METHOD_REGEX =
-            Pattern.compile("\\s*TEST_F\\(\\w+,\\s*(\\w+)\\).*");
+            Pattern.compile("\\s*TEST_F\\((\\w+),\\s*(\\w+)\\).*");
 
-    private void scanFile(File file, List<String> testNames) throws FileNotFoundException {
-        Scanner scanner = null;
+    public void scanFile(Scanner scanner, List<String> testNames) {
         try {
-            scanner = new Scanner(file);
+            String lastCase = "";
             while (scanner.hasNextLine()) {
                 String line = scanner.nextLine();
-                Matcher matcher = CLASS_REGEX.matcher(line);
-                if (matcher.matches()) {
-                    testNames.add("suite:" + mTestSuite);
-                    testNames.add("case:" + matcher.group(1));
+
+                Matcher matcher = METHOD_REGEX.matcher(line);
+
+                if (!matcher.matches()) {
                     continue;
                 }
 
-                matcher = METHOD_REGEX.matcher(line);
-                if (matcher.matches()) {
-                    testNames.add("test:" + matcher.group(1));
-                    continue;
+                if (!lastCase.equals(matcher.group(1))) {
+                    testNames.add("suite:" + mTestSuite);
+                    testNames.add("case:" + matcher.group(1));
+                    lastCase = matcher.group(1);
                 }
+
+                testNames.add("test:" + matcher.group(2));
             }
         } finally {
             if (scanner != null) {
diff --git a/suite/pts/hostTests/ptshostutil/Android.mk b/tools/cts-native-scanner/tests/Android.mk
similarity index 71%
copy from suite/pts/hostTests/ptshostutil/Android.mk
copy to tools/cts-native-scanner/tests/Android.mk
index 14e786a..6044b0d 100644
--- a/suite/pts/hostTests/ptshostutil/Android.mk
+++ b/tools/cts-native-scanner/tests/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2012 The Android Open Source Project
+# Copyright 2012 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -16,14 +16,13 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, src)
+# Only compile source java files in this lib
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed-prebuilt ddmlib-prebuilt junit
-
+LOCAL_MODULE := cts-native-scanner-tests
 LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE := ptshostutil
+LOCAL_JAVA_LIBRARIES := ddmlib-prebuilt tradefed-prebuilt cts-native-scanner
 
 include $(BUILD_HOST_JAVA_LIBRARY)
 
+
diff --git a/tools/cts-native-scanner/tests/run_unit_tests.sh b/tools/cts-native-scanner/tests/run_unit_tests.sh
new file mode 100755
index 0000000..a42e42b
--- /dev/null
+++ b/tools/cts-native-scanner/tests/run_unit_tests.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+# Copyright 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# helper script for running the cts-tradefed unit tests
+
+checkFile() {
+    if [ ! -f "$1" ]; then
+        echo "Unable to locate $1"
+        exit
+    fi;
+}
+
+JAR_DIR=${ANDROID_HOST_OUT}/framework
+JARS="ddmlib-prebuilt.jar tradefed-prebuilt.jar hosttestlib.jar cts-native-scanner.jar cts-native-scanner-tests.jar"
+
+for JAR in $JARS; do
+    checkFile ${JAR_DIR}/${JAR}
+    JAR_PATH=${JAR_PATH}:${JAR_DIR}/${JAR}
+done
+
+java $RDBG_FLAG \
+  -cp ${JAR_PATH} com.android.tradefed.command.Console run singleCommand host -n --class com.android.cts.nativescanner.UnitTests "$@"
+
diff --git a/tools/cts-native-scanner/tests/src/com/android/cts/nativescanner/TestScannerTest.java b/tools/cts-native-scanner/tests/src/com/android/cts/nativescanner/TestScannerTest.java
new file mode 100644
index 0000000..18732fd
--- /dev/null
+++ b/tools/cts-native-scanner/tests/src/com/android/cts/nativescanner/TestScannerTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.nativescanner;
+
+import com.android.cts.nativescanner.TestScanner;
+
+import junit.framework.TestCase;
+
+import java.io.File;
+import java.io.StringReader;
+import java.lang.StringBuilder;
+import java.util.Scanner;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * Unit tests for {@link TestScanner}.
+ */
+public class TestScannerTest extends TestCase {
+
+    public void testScanFile() {
+        TestScanner testScanner = new TestScanner(new File("unused"), "TestSuite");
+
+        String newLine = System.getProperty("line.separator");
+        StringBuilder sb = new StringBuilder();
+        sb.append("foobar" + newLine);  // ignored
+        sb.append("TEST_F(TestCase1, TestName1)" + newLine);  // valid
+        sb.append("TEST_F(TestCase1, TestName2)" + newLine);  // valid
+        sb.append("TEST_F(TestCase2, TestName1) foo" + newLine);  // valid
+        sb.append("TEST_F(TestCase2, TestName1 foo)" + newLine);  // ignored
+        sb.append("foo TEST_F(TestCase2, TestName1)" + newLine);  // ignored
+
+        List<String> names = new ArrayList<String>();
+        Scanner scanner = new Scanner(new StringReader(sb.toString()));
+        testScanner.scanFile(scanner, names);
+        Iterator it = names.iterator();
+
+        assertEquals("suite:TestSuite", it.next());
+        assertEquals("case:TestCase1", it.next());
+        assertEquals("test:TestName1", it.next());
+        assertEquals("test:TestName2", it.next());
+        assertEquals("suite:TestSuite", it.next());
+        assertEquals("case:TestCase2", it.next());
+        assertEquals("test:TestName1", it.next());
+        assertFalse(it.hasNext());
+        scanner.close();
+    }
+}
diff --git a/tools/cts-native-scanner/tests/src/com/android/cts/nativescanner/UnitTests.java b/tools/cts-native-scanner/tests/src/com/android/cts/nativescanner/UnitTests.java
new file mode 100644
index 0000000..13d4e5b
--- /dev/null
+++ b/tools/cts-native-scanner/tests/src/com/android/cts/nativescanner/UnitTests.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.nativescanner;
+
+import com.android.cts.nativescanner.TestScannerTest;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * A test suite for all cts-native-scanner unit tests.
+ * <p/>
+ * All tests listed here should be self-contained, and do not require any external dependencies
+ */
+public class UnitTests extends TestSuite {
+
+    public UnitTests() {
+        super();
+
+        // result package
+        addTestSuite(TestScannerTest.class);
+    }
+
+    public static Test suite() {
+        return new UnitTests();
+    }
+}
diff --git a/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/CtsXmlGenerator.java b/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/CtsXmlGenerator.java
index ce4fdd7..c5b253a 100644
--- a/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/CtsXmlGenerator.java
+++ b/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/CtsXmlGenerator.java
@@ -49,6 +49,8 @@
         String instrumentation = null;
         String testType = null;
         String jarPath = null;
+        String appNameSpace = null;
+        String targetNameSpace = null;
 
         for (int i = 0; i < args.length; i++) {
             if ("-p".equals(args[i])) {
@@ -68,15 +70,17 @@
                         "Missing value for expectation store")));
             } else if ("-o".equals(args[i])) {
                 outputPath = getArg(args, ++i, "Missing value for output file");
+            } else if ("-a".equals(args[i])) {
+                appNameSpace =  getArg(args, ++i, "Missing value for app name space");
+            } else if ("-r".equals(args[i])) {
+                targetNameSpace =  getArg(args, ++i, "Missing value for target name space");
             } else {
                 System.err.println("Unsupported flag: " + args[i]);
                 usage(args);
             }
         }
 
-        String appNameSpace = null;
         String runner = null;
-        String targetNameSpace = null;
 
         if (manifestFile != null) {
             Document manifest = DocumentBuilderFactory.newInstance().newDocumentBuilder()
diff --git a/tools/tradefed-host/.classpath b/tools/tradefed-host/.classpath
index 275ecf9..09dd0ce 100644
--- a/tools/tradefed-host/.classpath
+++ b/tools/tradefed-host/.classpath
@@ -3,10 +3,10 @@
 	<classpathentry kind="src" path="src"/>
 	<classpathentry kind="src" path="res"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/tradefederation"/>
 	<classpathentry exported="true" kind="var" path="CTS_SRC_ROOT/out/host/common/obj/JAVA_LIBRARIES/ctsdeviceinfolib_intermediates/javalib.jar"/>
 	<classpathentry exported="true" kind="var" path="CTS_SRC_ROOT/out/host/common/obj/JAVA_LIBRARIES/hosttestlib_intermediates/javalib.jar"/>
+	<classpathentry kind="var" path="CTS_SRC_ROOT/prebuilt/common/ddmlib/ddmlib-prebuilt.jar" sourcepath="/SDK_SRC_ROOT"/>
+	<classpathentry kind="var" path="CTS_SRC_ROOT/prebuilt/common/tradefed/tradefed-prebuilt.jar" sourcepath="/TRADEFED_ROOT/tools/tradefederation/src"/>
+	<classpathentry kind="var" path="CTS_SRC_ROOT/out/host/common/obj/JAVA_LIBRARIES/junit_intermediates/javalib.jar"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/tools/tradefed-host/.gitignore b/tools/tradefed-host/.gitignore
new file mode 100644
index 0000000..ba077a4
--- /dev/null
+++ b/tools/tradefed-host/.gitignore
@@ -0,0 +1 @@
+bin
diff --git a/tools/tradefed-host/Android.mk b/tools/tradefed-host/Android.mk
index 7af7993..e8965ab 100644
--- a/tools/tradefed-host/Android.mk
+++ b/tools/tradefed-host/Android.mk
@@ -17,13 +17,16 @@
 include $(CLEAR_VARS)
 
 # Only compile source java files in this lib.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := \
+	$(call all-java-files-under, src) \
+	$(call all-java-files-under, ../../suite/pts/hostTests/ptshostutil/src)
+
 LOCAL_JAVA_RESOURCE_DIRS := res
 
 LOCAL_MODULE := cts-tradefed
 LOCAL_MODULE_TAGS := optional
 LOCAL_JAVA_LIBRARIES := ddmlib-prebuilt tradefed-prebuilt hosttestlib
-LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceinfolib
+LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceinfolib ptscommonutilhost
 
 include $(BUILD_HOST_JAVA_LIBRARY)
 
diff --git a/tools/tradefed-host/res/report/cts_result.xsd b/tools/tradefed-host/res/report/cts_result.xsd
index 5e471d5..3f7f384 100644
--- a/tools/tradefed-host/res/report/cts_result.xsd
+++ b/tools/tradefed-host/res/report/cts_result.xsd
@@ -16,8 +16,8 @@
  -->
 
 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
-           targetNamespace="http://compatibility.android.com/cts_result/1.13"
-           xmlns="http://compatibility.android.com/cts_result/1.13"
+           targetNamespace="http://compatibility.android.com/cts_result/1.15"
+           xmlns="http://compatibility.android.com/cts_result/1.15"
            elementFormDefault="qualified">
 
 <xs:element name="TestResult">
@@ -32,6 +32,7 @@
     <xs:attribute name="endtime" type="xs:string"/>
     <xs:attribute name="testPlan" type="xs:string"/>
     <xs:attribute name="version" type="xs:string"/>
+    <xs:attribute name="suite" type="xs:string"/>
   </xs:complexType>
 </xs:element>
 
@@ -195,6 +196,29 @@
   <xs:attribute name="priority" type="xs:string"/>
 </xs:complexType>
 
+<xs:simpleType name="unitType">
+  <xs:restriction base="xs:string">
+    <xs:enumeration value="none"/>
+    <xs:enumeration value="ms"/>
+    <xs:enumeration value="fps"/>
+    <xs:enumeration value="ops"/>
+    <xs:enumeration value="kbps"/>
+    <xs:enumeration value="mbps"/>
+    <xs:enumeration value="byte"/>
+    <xs:enumeration value="count"/>
+    <xs:enumeration value="score"/>
+  </xs:restriction>
+</xs:simpleType>
+
+<xs:simpleType name="scoreTypeType">
+  <xs:restriction base="xs:string">
+    <xs:enumeration value="higherBetter"/>
+    <xs:enumeration value="lowerBetter"/>
+    <xs:enumeration value="neutral"/>
+    <xs:enumeration value="warning"/>
+  </xs:restriction>
+</xs:simpleType>
+
 <xs:complexType name="testType">
   <xs:sequence>
     <xs:element name="FailedScene" minOccurs="0" maxOccurs="1">
@@ -205,6 +229,35 @@
         <xs:attribute name="message" type="xs:string"/>
       </xs:complexType>
     </xs:element>
+    <xs:element name="Summary" minOccurs="0" maxOccurs="1">
+      <xs:complexType>
+        <xs:simpleContent>
+          <xs:extension base="xs:decimal">
+            <xs:attribute name="message" type="xs:string" use="required" />
+            <xs:attribute name="scoreType" type="scoreTypeType" use="required" />
+            <xs:attribute name="unit" type="unitType" use="required" />
+            <xs:attribute name="target" type="xs:decimal" />
+          </xs:extension>
+        </xs:simpleContent>
+      </xs:complexType>
+    </xs:element>
+    <xs:element name="Details" minOccurs="0" maxOccurs="1">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element name="ValueArray" minOccurs="0" maxOccurs="unbounded">
+            <xs:complexType>
+              <xs:sequence>
+                <xs:element name="Value" type="xs:decimal" minOccurs="0" maxOccurs="unbounded" />
+              </xs:sequence>
+              <xs:attribute name="source" type="xs:string" use="required" />
+              <xs:attribute name="message" type="xs:string" use="required" />
+              <xs:attribute name="scoreType" type="scoreTypeType" use="required" />
+              <xs:attribute name="unit" type="unitType" use="required" />
+            </xs:complexType>
+          </xs:element>
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
   </xs:sequence>
   <xs:attribute name="name" type="xs:string" use="required"/>
   <xs:attribute name="result" type="resultType" use="required"/>
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildHelper.java b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildHelper.java
index 95ec69f..61b4b43 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildHelper.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildHelper.java
@@ -30,18 +30,12 @@
 public class CtsBuildHelper {
 
     static final String CTS_DIR_NAME = "android-cts";
-    static final String PTS_DIR_NAME = "android-pts";
-    static private boolean mCtsMode = true;
-    private final String mSuiteName;
+    private final String mSuiteName = "CTS";
     /** The root location of the extracted CTS package */
     private final File mRootDir;
     /** the {@link CTS_DIR_NAME} directory */
     private final File mCtsDir;
 
-    public static void changeToPtsMode() {
-        mCtsMode = false;
-    }
-
     /**
      * Creates a {@link CtsBuildHelper}.
      *
@@ -50,8 +44,7 @@
      */
     public CtsBuildHelper(File rootDir) {
         mRootDir = rootDir;
-        mSuiteName = mCtsMode? "CTS" : "PTS";
-        mCtsDir = new File(mRootDir, mCtsMode ? CTS_DIR_NAME : PTS_DIR_NAME);
+        mCtsDir = new File(mRootDir, CTS_DIR_NAME);
     }
 
     /**
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/command/CtsConsole.java b/tools/tradefed-host/src/com/android/cts/tradefed/command/CtsConsole.java
index f014135..a8ca951 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/command/CtsConsole.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/command/CtsConsole.java
@@ -246,11 +246,6 @@
     }
 
     public static void main(String[] args) throws InterruptedException {
-        // change to PTS mode before anything else
-        String ptsMode = System.getProperty("PTS");
-        if ((ptsMode != null) && ptsMode.equals("1")) {
-            CtsBuildHelper.changeToPtsMode();
-        }
         Console console = new CtsConsole();
         Console.startConsole(console, args);
     }
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java
index a2a31e0..2f62d71 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java
@@ -58,7 +58,7 @@
     private static final String LOG_TAG = "CtsXmlResultReporter";
 
     static final String TEST_RESULT_FILE_NAME = "testResult.xml";
-    private static final String CTS_RESULT_FILE_VERSION = "1.13";
+    private static final String CTS_RESULT_FILE_VERSION = "1.14";
     private static final String[] CTS_RESULT_RESOURCES = {"cts_result.xsl", "cts_result.css",
         "logo.gif", "newrule-green.png"};
 
@@ -97,6 +97,7 @@
     private boolean mIsDeviceInfoRun = false;
     private ResultReporter mReporter;
     private File mLogDir;
+    private String mSuiteName;
 
     private static final String PTS_PERFORMANCE_EXCEPTION = "com.android.pts.util.PtsException";
     private static final Pattern mPtsLogPattern = Pattern.compile(
@@ -141,8 +142,8 @@
             mStartTime = getTimestamp();
             logResult("Created result dir %s", mReportDir.getName());
         }
-
-        mReporter = new ResultReporter(mResultServer, ctsBuildHelper.getSuiteName());
+        mSuiteName = ctsBuildHelper.getSuiteName();
+        mReporter = new ResultReporter(mResultServer, mSuiteName);
 
         // TODO: allow customization of log dir
         // create a unique directory for saving logs, with same name as result dir
@@ -358,6 +359,7 @@
         serializer.attribute(ns, STARTTIME_ATTR, startTime);
         serializer.attribute(ns, "endtime", endTime);
         serializer.attribute(ns, "version", CTS_RESULT_FILE_VERSION);
+        serializer.attribute(ns, "suite", mSuiteName);
 
         mResults.serialize(serializer);
         // TODO: not sure why, but the serializer doesn't like this statement
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/Test.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/Test.java
index dd4f31f..72e2e27 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/Test.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/Test.java
@@ -15,6 +15,7 @@
  */
 package com.android.cts.tradefed.result;
 
+import com.android.ddmlib.Log;
 import com.android.tradefed.result.TestResult;
 
 import org.kxml2.io.KXmlSerializer;
@@ -27,7 +28,6 @@
  * Data structure that represents a "Test" result XML element.
  */
 class Test extends AbstractXmlPullParser {
-
     static final String TAG = "Test";
     private static final String NAME_ATTR = "name";
     private static final String MESSAGE_ATTR = "message";
@@ -36,7 +36,17 @@
     private static final String RESULT_ATTR = "result";
     private static final String SCENE_TAG = "FailedScene";
     private static final String STACK_TAG = "StackTrace";
+    private static final String SUMMARY_TAG = "Summary";
     private static final String DETAILS_TAG = "Details";
+    private static final String VALUEARRAY_TAG = "ValueArray";
+    private static final String VALUE_TAG = "Value";
+    private static final String TARGET_ATTR = "target";
+    private static final String SCORETYPE_ATTR = "scoreType";
+    private static final String UNIT_ATTR = "unit";
+    private static final String SOURCE_ATTR = "source";
+    // separators for the message from PTS
+    private static final String LOG_SEPARATOR = "\\+\\+\\+";
+    private static final String LOG_ELEM_SEPARATOR = "\\|";
 
     private String mName;
     private CtsTestStatus mResult;
@@ -44,7 +54,8 @@
     private String mEndTime;
     private String mMessage;
     private String mStackTrace;
-    // details passed from pts
+    // summary and details passed from pts
+    private String mSummary;
     private String mDetails;
 
     /**
@@ -109,6 +120,14 @@
         mMessage = getFailureMessageFromStackTrace(mStackTrace);
     }
 
+    public String getSummary() {
+        return mSummary;
+    }
+
+    public void setSummary(String summary) {
+        mSummary = summary;
+    }
+
     public String getDetails() {
         return mDetails;
     }
@@ -147,17 +166,115 @@
                 serializer.text(mStackTrace);
                 serializer.endTag(CtsXmlResultReporter.ns, STACK_TAG);
             }
-            if (mDetails != null) {
-                serializer.startTag(CtsXmlResultReporter.ns, DETAILS_TAG);
-                serializer.text(mDetails);
-                serializer.endTag(CtsXmlResultReporter.ns, DETAILS_TAG);
-            }
             serializer.endTag(CtsXmlResultReporter.ns, SCENE_TAG);
         }
+        if (mSummary != null) {
+            // <Summary message = "screen copies per sec" scoretype="higherBetter" unit="fps">
+            // 23938.82978723404</Summary>
+            PerfResultSummary summary = parseSummary(mSummary);
+            if (summary != null) {
+                serializer.startTag(CtsXmlResultReporter.ns, SUMMARY_TAG);
+                serializer.attribute(CtsXmlResultReporter.ns, MESSAGE_ATTR, summary.mMessage);
+                if (summary.mTarget.length() != 0 && !summary.mTarget.equals(" ")) {
+                    serializer.attribute(CtsXmlResultReporter.ns, TARGET_ATTR, summary.mTarget);
+                }
+                serializer.attribute(CtsXmlResultReporter.ns, SCORETYPE_ATTR, summary.mType);
+                serializer.attribute(CtsXmlResultReporter.ns, UNIT_ATTR, summary.mUnit);
+                serializer.text(summary.mValue);
+                serializer.endTag(CtsXmlResultReporter.ns, SUMMARY_TAG);
+                // add details only if summary is present
+                // <Details>
+                //   <ValueArray source=”com.android.pts.dram.BandwidthTest#doRunMemcpy:98”
+                //                    message=”measure1” unit="ms" scoretype="higherBetter">
+                //     <Value>0.0</Value>
+                //     <Value>0.1</Value>
+                //   </ValueArray>
+                // </Details>
+                if (mDetails != null) {
+                    PerfResultDetail[] ds = parseDetails(mDetails);
+                    serializer.startTag(CtsXmlResultReporter.ns, DETAILS_TAG);
+                        for (PerfResultDetail d : ds) {
+                            if (d == null) {
+                                continue;
+                            }
+                            serializer.startTag(CtsXmlResultReporter.ns, VALUEARRAY_TAG);
+                            serializer.attribute(CtsXmlResultReporter.ns, SOURCE_ATTR, d.mSource);
+                            serializer.attribute(CtsXmlResultReporter.ns, MESSAGE_ATTR,
+                                    d.mMessage);
+                            serializer.attribute(CtsXmlResultReporter.ns, SCORETYPE_ATTR, d.mType);
+                            serializer.attribute(CtsXmlResultReporter.ns, UNIT_ATTR, d.mUnit);
+                            for (String v : d.mValues) {
+                                if (v == null) {
+                                    continue;
+                                }
+                                serializer.startTag(CtsXmlResultReporter.ns, VALUE_TAG);
+                                serializer.text(v);
+                                serializer.endTag(CtsXmlResultReporter.ns, VALUE_TAG);
+                            }
+                            serializer.endTag(CtsXmlResultReporter.ns, VALUEARRAY_TAG);
+                        }
+                    serializer.endTag(CtsXmlResultReporter.ns, DETAILS_TAG);
+                }
+            }
+        }
         serializer.endTag(CtsXmlResultReporter.ns, TAG);
     }
 
     /**
+     *  class containing performance result.
+     */
+    public static class PerfResultCommon {
+        public String mMessage;
+        public String mType;
+        public String mUnit;
+    }
+
+    private class PerfResultSummary extends PerfResultCommon {
+        public String mTarget;
+        public String mValue;
+    }
+
+    private class PerfResultDetail extends PerfResultCommon {
+        public String mSource;
+        public String[] mValues;
+    }
+
+    private PerfResultSummary parseSummary(String summary) {
+        String[] elems = summary.split(LOG_ELEM_SEPARATOR);
+        PerfResultSummary r = new PerfResultSummary();
+        if (elems.length < 5) {
+            Log.w(TAG, "wrong message " + summary);
+            return null;
+        }
+        r.mMessage = elems[0];
+        r.mTarget = elems[1];
+        r.mType = elems[2];
+        r.mUnit = elems[3];
+        r.mValue = elems[4];
+        return r;
+    }
+
+    private PerfResultDetail[] parseDetails(String details) {
+        String[] arrays = details.split(LOG_SEPARATOR);
+        PerfResultDetail[] rs = new PerfResultDetail[arrays.length];
+        for (int i = 0; i < arrays.length; i++) {
+            String[] elems = arrays[i].split(LOG_ELEM_SEPARATOR);
+            if (elems.length < 5) {
+                Log.w(TAG, "wrong message " + arrays[i]);
+                continue;
+            }
+            PerfResultDetail r = new PerfResultDetail();
+            r.mSource = elems[0];
+            r.mMessage = elems[1];
+            r.mType = elems[2];
+            r.mUnit = elems[3];
+            r.mValues = elems[4].split(" ");
+            rs[i] = r;
+        }
+        return rs;
+    }
+
+    /**
      * Strip out any invalid XML characters that might cause the report to be unviewable.
      * http://www.w3.org/TR/REC-xml/#dt-character
      */
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java
index ce1c2f3..015a7ae 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java
@@ -283,7 +283,7 @@
     public void reportPerformanceResult(TestIdentifier test, CtsTestStatus status, String summary, String details) {
         Test result = findTest(test);
         result.setResultStatus(status);
-        result.setMessage(summary);
+        result.setSummary(summary);
         result.setDetails(details);
     }
 
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/targetprep/SettingsToggler.java b/tools/tradefed-host/src/com/android/cts/tradefed/targetprep/SettingsToggler.java
index 69d4c8a..ff6c4f4 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/targetprep/SettingsToggler.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/targetprep/SettingsToggler.java
@@ -22,52 +22,98 @@
  * {@link SettingsToggler} sets settings by using the "adb shell content" command.
  */
 public class SettingsToggler {
+    private static final String GROUP_SECURE = "secure";
+    private static final String GROUP_GLOBAL = "global";
+
+    /** Sets a setting by deleting and then inserting the string value. */
+    public static void setString(ITestDevice device, String group, String name, String value)
+            throws DeviceNotAvailableException {
+        deleteSetting(device, group, name);
+        device.executeShellCommand(
+                "content insert"
+                + " --uri content://settings/" + group
+                + " --bind name:s:" + name
+                + " --bind value:s:" + value);
+    }
 
     /** Sets a secure setting by deleting and then inserting the string value. */
     public static void setSecureString(ITestDevice device, String name, String value)
             throws DeviceNotAvailableException {
-        deleteSecure(device, name);
+        setString(device, GROUP_SECURE, name, value);
+    }
+
+    /** Sets a global setting by deleting and then inserting the string value. */
+    public static void setGlobalString(ITestDevice device, String name, String value)
+            throws DeviceNotAvailableException {
+        setString(device, GROUP_GLOBAL, name, value);
+    }
+
+    /** Sets a setting by deleting and then inserting the int value. */
+    public static void setInt(ITestDevice device, String group, String name, int value)
+            throws DeviceNotAvailableException {
+        deleteSetting(device, group, name);
         device.executeShellCommand(
                 "content insert"
-                + " --uri content://settings/secure"
+                + " --uri content://settings/" + group
                 + " --bind name:s:" + name
-                + " --bind value:s:" + value);
+                + " --bind value:i:" + value);
     }
 
     /** Sets a secure setting by deleting and then inserting the int value. */
     public static void setSecureInt(ITestDevice device, String name, int value)
             throws DeviceNotAvailableException {
-        deleteSecure(device, name);
+        setInt(device, GROUP_SECURE, name, value);
+    }
+
+    /** Sets a global setting by deleting and then inserting the int value. */
+    public static void setGlobalInt(ITestDevice device, String name, int value)
+            throws DeviceNotAvailableException {
+        setInt(device, GROUP_GLOBAL, name, value);
+    }
+
+    public static void updateString(ITestDevice device, String group, String name, String value)
+            throws DeviceNotAvailableException {
         device.executeShellCommand(
-                "content insert"
-                + " --uri content://settings/secure"
-                + " --bind name:s:" + name
-                + " --bind value:i:" + value);
+                "content update"
+                + " --uri content://settings/" + group
+                + " --bind value:s:" + value
+                + " --where \"name='" + name + "'\"");
     }
 
     public static void updateSecureString(ITestDevice device, String name, String value)
             throws DeviceNotAvailableException {
+        updateString(device, GROUP_SECURE, name, value);
+    }
+
+    public static void updateGlobalString(ITestDevice device, String name, String value)
+            throws DeviceNotAvailableException {
+        updateString(device, GROUP_GLOBAL, name, value);
+    }
+
+    public static void updateInt(ITestDevice device, String group, String name, int value)
+            throws DeviceNotAvailableException {
         device.executeShellCommand(
                 "content update"
-                + " --uri content://settings/secure"
-                + " --bind value:s:" + value
+                + " --uri content://settings/" + group
+                + " --bind value:i:" + value
                 + " --where \"name='" + name + "'\"");
     }
 
     public static void updateSecureInt(ITestDevice device, String name, int value)
             throws DeviceNotAvailableException {
-        device.executeShellCommand(
-                "content update"
-                + " --uri content://settings/secure"
-                + " --bind value:i:" + value
-                + " --where \"name='" + name + "'\"");
+        updateInt(device, GROUP_SECURE, name, value);
     }
 
-    private static void deleteSecure(ITestDevice device, String name)
+    public static void updateGlobalInt(ITestDevice device, String name, int value)
+            throws DeviceNotAvailableException {
+        updateInt(device, GROUP_GLOBAL, name, value);
+    }
+
+    private static void deleteSetting(ITestDevice device, String group, String name)
             throws DeviceNotAvailableException {
         device.executeShellCommand(
                 "content delete"
-                + " --uri content://settings/secure"
+                + " --uri content://settings/" + group
                 + " --where \"name='" + name + "'\"");
     }
 }
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DisplayTestRunner.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DisplayTestRunner.java
new file mode 100644
index 0000000..4c83aa9
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DisplayTestRunner.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.tradefed.testtype;
+
+import com.android.cts.tradefed.targetprep.SettingsToggler;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.result.ITestInvocationListener;
+
+/**
+ * Running the display tests requires modification of secure settings to create an overlay display.
+ * Secure settings cannot be changed from device CTS tests since system signature permission is
+ * required. Such settings can be modified by the shell user, so a host side test is used.
+ */
+public class DisplayTestRunner extends InstrumentationApkTest {
+    private static final String OVERLAY_DISPLAY_DEVICES_SETTING_NAME = "overlay_display_devices";
+
+    // Use a non-standard pattern, must match values in tests/tests/display/.../DisplayTest.java
+    private static final String OVERLAY_DISPLAY_DEVICES_SETTING_VALUE = "1281x721/214";
+
+    @Override
+    public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
+        // CLog.e("run: About to enable overlay display.");
+        SettingsToggler.setGlobalString(getDevice(), OVERLAY_DISPLAY_DEVICES_SETTING_NAME,
+                OVERLAY_DISPLAY_DEVICES_SETTING_VALUE);
+
+        super.run(listener);
+
+        // Tear down overlay display.
+        // CLog.e("run: About to disable overlay display.");
+        SettingsToggler.setGlobalString(getDevice(), OVERLAY_DISPLAY_DEVICES_SETTING_NAME,
+                "");
+    }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java
index ffbb930..2d11657 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java
@@ -16,6 +16,7 @@
 
 package com.android.cts.tradefed.testtype;
 
+import com.android.ddmlib.Log.LogLevel;
 import com.android.ddmlib.testrunner.TestIdentifier;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.testtype.IRemoteTest;
@@ -43,11 +44,15 @@
 
     public static final String HOST_SIDE_ONLY_TEST = "hostSideOnly";
     public static final String NATIVE_TEST = "native";
+    public static final String WRAPPED_NATIVE_TEST = "wrappednative";
     public static final String VM_HOST_TEST = "vmHostTest";
     public static final String ACCESSIBILITY_TEST =
         "com.android.cts.tradefed.testtype.AccessibilityTestRunner";
     public static final String ACCESSIBILITYSERVICE_TEST =
         "com.android.cts.tradefed.testtype.AccessibilityServiceTestRunner";
+    public static final String DISPLAY_TEST =
+            "com.android.cts.tradefed.testtype.DisplayTestRunner";
+    public static final String UIAUTOMATOR_TEST = "uiAutomator";
 
     private static final String SIGNATURE_TEST_METHOD = "testSignature";
     private static final String SIGNATURE_TEST_CLASS = "android.tests.sigtest.SimpleSignatureTest";
@@ -219,12 +224,21 @@
             return vmHostTest;
         } else if (NATIVE_TEST.equals(mTestType)) {
             return new GeeTest(mUri, mName);
+        } else if (WRAPPED_NATIVE_TEST.equals(mTestType)) {
+            CLog.d("Creating new wrapped native test for %s", mName);
+            return new WrappedGTest(mAppNameSpace, mUri, mName, mRunner);
         } else if (ACCESSIBILITY_TEST.equals(mTestType)) {
             AccessibilityTestRunner test = new AccessibilityTestRunner();
             return setInstrumentationTest(test, testCaseDir);
         } else if (ACCESSIBILITYSERVICE_TEST.equals(mTestType)) {
             AccessibilityServiceTestRunner test = new AccessibilityServiceTestRunner();
             return setInstrumentationTest(test, testCaseDir);
+        } else if (DISPLAY_TEST.equals(mTestType)) {
+            DisplayTestRunner test = new DisplayTestRunner();
+            return setInstrumentationTest(test, testCaseDir);
+        } else if (UIAUTOMATOR_TEST.equals(mTestType)) {
+            UiAutomatorJarTest uiautomatorTest = new UiAutomatorJarTest();
+            return setUiAutomatorTest(uiautomatorTest);
         } else if (mIsSignatureTest) {
             // TODO: hardcode the runner/class/method for now, since current package xml points to
             // specialized instrumentation. Eventually this special case for signatureTest can be
@@ -285,6 +299,28 @@
     }
 
     /**
+     * Populates given {@link UiAutomatorJarTest} with data from the package xml.
+     *
+     * @param uiautomatorTest
+     * @return the populated {@link UiAutomatorJarTest} or <code>null</code>
+     */
+    private IRemoteTest setUiAutomatorTest(UiAutomatorJarTest uiautomatorTest) {
+        uiautomatorTest.setInstallArtifacts(getJarPath());
+        if (mClassName != null) {
+            if (mMethodName != null) {
+                CLog.logAndDisplay(LogLevel.WARN, "ui automator tests don't currently support" +
+                        "running  individual methods");
+            }
+            uiautomatorTest.addClassName(mClassName);
+        } else {
+            uiautomatorTest.addClassNames(mTestClasses);
+        }
+        uiautomatorTest.setRunName(getUri());
+        uiautomatorTest.setCaptureLogs(false);
+        return uiautomatorTest;
+    }
+
+    /**
      * Filter the tests to run based on list of excluded tests, class and method name.
      *
      * @return the filtered collection of tests
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/UiAutomatorJarTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/UiAutomatorJarTest.java
new file mode 100644
index 0000000..83cc6d6
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/UiAutomatorJarTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.tradefed.testtype;
+
+import com.android.cts.tradefed.build.CtsBuildHelper;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.testtype.UiAutomatorTest;
+
+import java.io.FileNotFoundException;
+import java.util.Arrays;
+
+import junit.framework.Assert;
+
+/**
+ * A {@link UiAutomatorTest} that will install a uiautomator jar before test
+ * execution, and uninstall on execution completion.
+ */
+public class UiAutomatorJarTest extends UiAutomatorTest implements IBuildReceiver {
+
+    // TODO: expose this in parent
+    private static final String SHELL_EXE_BASE = "/data/local/tmp/";
+
+    /** the file names of the CTS jar to install */
+    private String mTestJarFileName;
+
+    private CtsBuildHelper mCtsBuild = null;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setBuild(IBuildInfo build) {
+        mCtsBuild  = CtsBuildHelper.createBuildHelper(build);
+    }
+
+    /**
+     * Setter for CTS build files needed to perform the test. 
+     *
+     * @param testJarName the file name of the jar containing the uiautomator tests
+     */
+    public void setInstallArtifacts(String testJarName) {
+        mTestJarFileName = testJarName;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void run(final ITestInvocationListener listener)
+            throws DeviceNotAvailableException {
+        Assert.assertNotNull("missing device", getDevice());
+        Assert.assertNotNull("missing build", mCtsBuild);
+        Assert.assertNotNull("missing jar to install", mTestJarFileName);
+
+        installJar();
+
+        super.run(listener);
+
+        uninstallJar();
+    }
+
+    private void installJar() throws DeviceNotAvailableException {
+        CLog.d("Installing %s on %s", mTestJarFileName, getDevice().getSerialNumber());
+        String fullJarPath = String.format("%s%s", SHELL_EXE_BASE, mTestJarFileName);
+        try {
+            boolean result = getDevice().pushFile(mCtsBuild.getTestApp(mTestJarFileName),
+                    fullJarPath);
+            Assert.assertTrue(String.format("Failed to push file to %s", fullJarPath), result);
+            setTestJarPaths(Arrays.asList(fullJarPath));
+        }  catch (FileNotFoundException e) {
+            Assert.fail(String.format("Could not find file %s", mTestJarFileName));
+        }
+    }
+
+    private void uninstallJar() throws DeviceNotAvailableException {
+        CLog.d("Uninstalling %s on %s", mTestJarFileName, getDevice().getSerialNumber());
+        String fullJarPath = String.format("%s%s", SHELL_EXE_BASE, mTestJarFileName);
+        getDevice().executeShellCommand(String.format("rm %s", fullJarPath));
+    }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/WrappedGTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/WrappedGTest.java
new file mode 100644
index 0000000..74c15f6
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/WrappedGTest.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.tradefed.testtype;
+
+import com.android.cts.tradefed.build.CtsBuildHelper;
+import com.android.ddmlib.testrunner.ITestRunListener;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.testtype.IDeviceTest;
+import com.android.tradefed.testtype.IRemoteTest;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+/**
+ * Test runner for wrapped (native) GTests
+ */
+public class WrappedGTest implements IBuildReceiver, IDeviceTest, IRemoteTest {
+
+    private int mMaxTestTimeMs = 1 * 60 * 1000;
+
+    private CtsBuildHelper mCtsBuild;
+    private ITestDevice mDevice;
+
+    private final String mAppNameSpace;
+    private final String mRunner;
+    private final String mName;
+    private final String mUri;
+
+
+    public WrappedGTest(String appNameSpace, String uri, String name, String runner) {
+        mAppNameSpace = appNameSpace;
+        mRunner = runner;
+        mName = name;
+        mUri = uri;
+    }
+
+    @Override
+    public void setBuild(IBuildInfo buildInfo) {
+        mCtsBuild = CtsBuildHelper.createBuildHelper(buildInfo);
+    }
+
+    @Override
+    public void setDevice(ITestDevice device) {
+        mDevice = device;
+    }
+
+    @Override
+    public ITestDevice getDevice() {
+        return mDevice;
+    }
+
+    @Override
+    public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
+        if (installTest()) {
+            runTest(listener);
+            uninstallTest();
+        } else {
+            CLog.e("Failed to install test");
+        }
+    }
+
+    private boolean installTest() throws DeviceNotAvailableException {
+        try {
+            File testApp = mCtsBuild.getTestApp(String.format("%s.apk", mName));
+            String installCode = mDevice.installPackage(testApp, true);
+
+            if (installCode != null) {
+                CLog.e("Failed to install %s.apk on %s. Reason: %s", mName,
+                    mDevice.getSerialNumber(), installCode);
+                return false;
+            }
+        }
+        catch (FileNotFoundException e) {
+            CLog.e("Package %s.apk not found", mName);
+            return false;
+        }
+        return true;
+    }
+
+    private void runTest(ITestRunListener listener) throws DeviceNotAvailableException {
+        WrappedGTestResultParser resultParser = new WrappedGTestResultParser(mUri, listener);
+        resultParser.setFakePackagePrefix(mUri + ".");
+        try {
+            String command = String.format("am instrument -w %s/.%s", mAppNameSpace, mRunner);
+            mDevice.executeShellCommand(command, resultParser, mMaxTestTimeMs, 0);
+        } catch (DeviceNotAvailableException e) {
+            resultParser.flush();
+            throw e;
+        } catch (RuntimeException e) {
+            resultParser.flush();
+            throw e;
+        }
+    }
+
+    private void uninstallTest() throws DeviceNotAvailableException {
+        mDevice.uninstallPackage(mAppNameSpace);
+    }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/WrappedGTestResultParser.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/WrappedGTestResultParser.java
new file mode 100644
index 0000000..cc3c53a
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/WrappedGTestResultParser.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.tradefed.testtype;
+
+import com.android.ddmlib.testrunner.ITestRunListener;
+import com.android.tradefed.log.LogUtil.CLog;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.ArrayList;
+
+public class WrappedGTestResultParser extends GeeTestResultParser {
+
+    private boolean mInstrumentationError;
+
+    /**
+     * Creates the WrappedGTestResultParser.
+     *
+     * @param testRunName the test run name to provide to
+     *            {@link ITestRunListener#testRunStarted(String, int)}
+     * @param listeners informed of test results as the tests are executing
+     */
+    public WrappedGTestResultParser(String testRunName, Collection<ITestRunListener> listeners) {
+        super(testRunName, listeners);
+    }
+
+    /**
+     * Creates the WrappedGTestResultParser for a single listener.
+     *
+     * @param testRunName the test run name to provide to
+     *            {@link ITestRunListener#testRunStarted(String, int)}
+     * @param listener informed of test results as the tests are executing
+     */
+    public WrappedGTestResultParser(String testRunName, ITestRunListener listener) {
+        super(testRunName, listener);
+    }
+
+    /**
+     * Strips the instrumentation information and then forwards
+     * the raw gtest output to the {@link GeeTestResultParser}.
+     */
+    @Override
+    public void processNewLines(String[] lines) {
+        if (mInstrumentationError) {
+            return;
+        }
+
+        String[] gtestOutput = parseInstrumentation(lines);
+        super.processNewLines(gtestOutput);
+    }
+
+    /**
+     * Parses raw instrumentation output and returns the
+     * contained gtest output
+     *
+     * @param lines the raw instrumentation output
+     * @return the gtest output
+     */
+    public String[] parseInstrumentation(String[] lines) {
+        List<String> output = new ArrayList<String>();
+        boolean readMultiLine = false;
+        for (String line : lines) {
+
+            if (line.startsWith("INSTRUMENTATION_RESULT: ")) {
+                CLog.e("Instrumentation Error:");
+                mInstrumentationError = true;
+            }
+
+            if (mInstrumentationError) {
+                CLog.e(line);
+                continue;
+            }
+
+            if (line.startsWith("INSTRUMENTATION_STATUS: gtest=")) {
+                output.add(line.replace("INSTRUMENTATION_STATUS: gtest=", ""));
+                readMultiLine = true;
+                continue;
+            }
+
+            if (line.startsWith("INSTRUMENTATION_")) {
+                readMultiLine = false;
+                continue;
+            }
+
+            if (readMultiLine) {
+                output.add(line);
+            }
+        }
+
+        return output.toArray(new String[output.size()]);
+    }
+}
+
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/UnitTests.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/UnitTests.java
index 7dfac13..3c36a3d 100644
--- a/tools/tradefed-host/tests/src/com/android/cts/tradefed/UnitTests.java
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/UnitTests.java
@@ -27,6 +27,7 @@
 import com.android.cts.tradefed.testtype.TestPackageDefTest;
 import com.android.cts.tradefed.testtype.TestPackageXmlParserTest;
 import com.android.cts.tradefed.testtype.TestPlanTest;
+import com.android.cts.tradefed.testtype.WrappedGTestResultParserTest;
 
 import junit.framework.Test;
 import junit.framework.TestSuite;
@@ -57,6 +58,7 @@
         addTestSuite(TestPackageDefTest.class);
         addTestSuite(TestPackageXmlParserTest.class);
         addTestSuite(TestPlanTest.class);
+        addTestSuite(WrappedGTestResultParserTest.class);
     }
 
     public static Test suite() {
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/WrappedGTestResultParserTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/WrappedGTestResultParserTest.java
new file mode 100644
index 0000000..d386c7a
--- /dev/null
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/WrappedGTestResultParserTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.tradefed.testtype;
+
+import com.android.ddmlib.testrunner.ITestRunListener;
+
+import junit.framework.TestCase;
+
+
+
+/**
+ * Unit tests for {@link WrappedGTestResultParser}.
+ */
+public class WrappedGTestResultParserTest extends TestCase {
+
+    private WrappedGTestResultParser mParser;
+    private final String[] INPUT1 = new String[] {
+        "INSTRUMENTATION_STATUS: gtest=[==========] Running 9 tests from 2 test cases.",
+        "INSTRUMENTATION_STATUS_CODE: 1",
+        "INSTRUMENTATION_STATUS: gtest=[ RUN      ] GLTest.Test1",
+        "INSTRUMENTATION_STATUS: gtest=[       OK ] GLTest.Test1 (10 ms)",
+        "INSTRUMENTATION_STATUS: gtest=/tests/SomeTestFile.cpp:1337: Failure",
+        "Value of: 1 == 0",
+        "  Actual: false",
+        "Expected: true",
+        "INSTRUMENTATION_STATUS: gtest=[  FAILED  ] GLTest.Test2 (1016 ms)",
+        "INSTRUMENTATION_STATUS: gtest=[==========] 2 tests from 1 test cases ran. (17 ms total)",
+        "INSTRUMENTATION_CODE: -1"
+    };
+
+    private final String[] EXPECTED_OUTPUT1 = new String[] {
+        "[==========] Running 9 tests from 2 test cases.",
+        "[ RUN      ] GLTest.Test1",
+        "[       OK ] GLTest.Test1 (10 ms)",
+        "/tests/SomeTestFile.cpp:1337: Failure",
+        "Value of: 1 == 0",
+        "  Actual: false",
+        "Expected: true",
+        "[  FAILED  ] GLTest.Test2 (1016 ms)",
+        "[==========] 2 tests from 1 test cases ran. (17 ms total)",
+    };
+
+    private final String[] INPUT2 = new String[] {
+        "INSTRUMENTATION_STATUS_CODE: 1",
+        "invalid text",
+        "INSTRUMENTATION_STATUS: gtest=[==========] Running 9 tests from 2 test cases.",
+        "INSTRUMENTATION_RESULT: some error",
+        "INSTRUMENTATION_STATUS: gtest=[ RUN      ] GLTest.ExpectTestThatShouldBeSuccessful",
+    };
+
+    private final String[] EXPECTED_OUTPUT2 = new String[] {
+        "[==========] Running 9 tests from 2 test cases.",
+    };
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mParser = new WrappedGTestResultParser("unused", (ITestRunListener)null);
+    }
+
+    private void assertArrayEquals(String[] expected, String[] result) throws Exception {
+        if (expected == null) {
+            assertNull(result);
+            return;
+        }
+
+        assertEquals(expected.length, result.length);
+
+        for (int i = 0; i < expected.length; i++) {
+            assertEquals(expected[i], result[i]);
+        }
+    }
+
+    /**
+     * Test normal case {@link WrappedGTestResultParser#getRawGTestOutput(java.lang.String[])}
+     * with all kinds of valid input lines.
+     */
+    public void testGetRawGTestOutput_valid() throws Exception {
+        assertArrayEquals(EXPECTED_OUTPUT1, mParser.parseInstrumentation(INPUT1));
+    }
+
+    /**
+     * Test normal case {@link WrappedGTestResultParser#getRawGTestOutput(java.lang.String[])}
+     * with a instrumentation error/invalid input.
+     */
+    public void testGetRawGTestOutput_invalid() throws Exception {
+        assertArrayEquals(EXPECTED_OUTPUT2, mParser.parseInstrumentation(INPUT2));
+    }
+}
diff --git a/tools/utils/buildCts.py b/tools/utils/buildCts.py
index 8e23f13..6dc1c70 100755
--- a/tools/utils/buildCts.py
+++ b/tools/utils/buildCts.py
@@ -54,24 +54,20 @@
 
   def __init__(self, argv):
     """Initialize the CtsBuilder from command line arguments."""
-    if not (len(argv) == 6 or len(argv)==7):
-      print 'Usage: %s <testRoot> <ctsOutputDir> <tempDir> <androidRootDir> <docletPath> [-pts]' % argv[0]
+    if len(argv) != 6:
+      print 'Usage: %s <testRoot> <ctsOutputDir> <tempDir> <androidRootDir> <docletPath>' % argv[0]
       print ''
       print 'testRoot:       Directory under which to search for CTS tests.'
       print 'ctsOutputDir:   Directory in which the CTS repository should be created.'
       print 'tempDir:        Directory to use for storing temporary files.'
       print 'androidRootDir: Root directory of the Android source tree.'
       print 'docletPath:     Class path where the DescriptionGenerator doclet can be found.'
-      print '-pts:           generate plan for PTS.'
       sys.exit(1)
     self.test_root = sys.argv[1]
     self.out_dir = sys.argv[2]
     self.temp_dir = sys.argv[3]
     self.android_root = sys.argv[4]
     self.doclet_path = sys.argv[5]
-    self.isCts = True
-    if len(argv) ==7 and sys.argv[6] == "-pts":
-      self.isCts = False
 
     self.test_repository = os.path.join(self.out_dir, 'repository/testcases')
     self.plan_repository = os.path.join(self.out_dir, 'repository/plans')
@@ -103,35 +99,43 @@
       doc = tools.XmlFile(description)
       packages.append(doc.GetAttr('TestPackage', 'appPackageName'))
 
-    if not self.isCts: # PTS
-      plan = tools.TestPlan(packages)
-      plan.Include('.*')
-      plan.Exclude(r'android\.tests\.sigtest')
-      self.__WritePlan(plan, 'PTS')
-      return
+    ptsPattern = r'com\.android\.pts\..*'
+    plan = tools.TestPlan(packages)
+    plan.Exclude('.*')
+    plan.Include(ptsPattern)
+    self.__WritePlan(plan, 'PTS')
 
     plan = tools.TestPlan(packages)
     plan.Exclude('android\.performance.*')
     self.__WritePlan(plan, 'CTS')
     self.__WritePlan(plan, 'CTS-TF')
 
+    plan = tools.TestPlan(packages)
+    plan.Exclude(ptsPattern)
+    plan.Exclude('android\.performance.*')
+    self.__WritePlan(plan, 'SDK')
+
     plan.Exclude(r'android\.tests\.sigtest')
     plan.Exclude(r'android\.core.*')
     self.__WritePlan(plan, 'Android')
 
     plan = tools.TestPlan(packages)
+    plan.Exclude(ptsPattern)
     plan.Include(r'android\.core\.tests.*')
     self.__WritePlan(plan, 'Java')
 
     plan = tools.TestPlan(packages)
+    plan.Exclude(ptsPattern)
     plan.Include(r'android\.core\.vm-tests-tf')
     self.__WritePlan(plan, 'VM-TF')
 
     plan = tools.TestPlan(packages)
+    plan.Exclude(ptsPattern)
     plan.Include(r'android\.tests\.sigtest')
     self.__WritePlan(plan, 'Signature')
 
     plan = tools.TestPlan(packages)
+    plan.Exclude(ptsPattern)
     plan.Include(r'android\.tests\.appsecurity')
     self.__WritePlan(plan, 'AppSecurity')
 
@@ -165,9 +169,8 @@
 
 if __name__ == '__main__':
   builder = CtsBuilder(sys.argv)
-  if builder.isCts:
-    result = builder.GenerateTestDescriptions()
-    if result != 0:
-      sys.exit(result)
+  result = builder.GenerateTestDescriptions()
+  if result != 0:
+    sys.exit(result)
   builder.GenerateTestPlans()