am da1bdb40: am 5cd66baf: Merge "Add a test for a dalvik interface cache bug."

* commit 'da1bdb40e08de6ceae1b6e6735ff3549b937f26e':
  Add a test for a dalvik interface cache bug.
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..933a961 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -38,6 +38,7 @@
 	CtsTestStubs \
 	SignatureTest \
 	TestDeviceSetup \
+        CtsUiAutomatorApp \
 	$(cts_security_apps_list)
 
 cts_external_packages := \
@@ -62,8 +63,10 @@
 	CtsCalendarcommon2TestCases \
 	CtsContentTestCases \
 	CtsDatabaseTestCases \
+	CtsDisplayTestCases \
 	CtsDpiTestCases \
 	CtsDpiTestCases2 \
+	CtsDreamsTestCases \
 	CtsDrmTestCases \
 	CtsEffectTestCases \
 	CtsExampleTestCases \
@@ -76,6 +79,7 @@
 	CtsLocationTestCases \
 	CtsMediaStressTestCases \
 	CtsMediaTestCases \
+	CtsNativeOpenGLTestCases \
 	CtsNdefTestCases \
 	CtsNetTestCases \
 	CtsOpenGLTestCases \
@@ -115,17 +119,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/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_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..ac338dd 100755
--- a/libs/testserver/src/android/webkit/cts/CtsTestServer.java
+++ b/libs/testserver/src/android/webkit/cts/CtsTestServer.java
@@ -544,12 +544,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()) {
diff --git a/libs/wrappedgtest/Android.mk b/libs/wrappedgtest/Android.mk
new file mode 100644
index 0000000..f89ba9d
--- /dev/null
+++ b/libs/wrappedgtest/Android.mk
@@ -0,0 +1,25 @@
+# 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_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_MODULE_TAGS := optional
+
+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/PtsBuild.mk b/suite/pts/PtsBuild.mk
index 18306b9..23e6d58 100644
--- a/suite/pts/PtsBuild.mk
+++ b/suite/pts/PtsBuild.mk
@@ -22,7 +22,8 @@
 PTS_TEST_PACKAGES := \
     PtsDeviceFilePerf \
     PtsDeviceUi \
-    PtsDeviceDram
+    PtsDeviceDram \
+    PtsDeviceSimpleCpu
 
 
 PTS_SUPPORT_PACKAGES := \
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..d675edc 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,7 @@
 import android.util.Log;
 import android.view.WindowManager;
 
+import com.android.pts.util.PerfResultType;
 import com.android.pts.util.PtsAndroidTestCase;
 import com.android.pts.util.ReportLog;
 import com.android.pts.util.Stat;
@@ -109,6 +110,6 @@
         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);
+                PerfResultType.HIGHER_BETTER, screensPerSecStddev);
     }
 }
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..1e39441 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
@@ -28,6 +28,7 @@
 
 import com.android.pts.util.MeasureRun;
 import com.android.pts.util.MeasureTime;
+import com.android.pts.util.PerfResultType;
 import com.android.pts.util.ReportLog;
 import com.android.pts.util.Stat;
 import com.android.pts.util.SystemUtil;
@@ -303,7 +304,7 @@
         report.printArray("Rd amount", rdAmount, true);
         Stat.StatResult stat = Stat.getStat(mbps);
 
-        report.printSummary("MB/s", stat.mAverage, stat.mStddev);
+        report.printSummary("MB/s", stat.mAverage, PerfResultType.HIGHER_BETTER, stat.mStddev);
     }
 
     /**
@@ -353,7 +354,7 @@
         report.printArray("Wr amount", wrAmount, true);
         Stat.StatResult stat = Stat.getStat(mbps);
 
-        report.printSummary("MB/s", stat.mAverage, stat.mStddev);
+        report.printSummary("MB/s", stat.mAverage, PerfResultType.HIGHER_BETTER, stat.mStddev);
     }
 
     /**
@@ -391,6 +392,6 @@
             ReportLog.copyArray(mbps, mbpsAll, i * numberRepeatInOneRun);
         }
         Stat.StatResult stat = Stat.getStat(mbpsAll);
-        report.printSummary("MB/s", stat.mAverage, stat.mStddev);
+        report.printSummary("MB/s", stat.mAverage, PerfResultType.HIGHER_BETTER, stat.mStddev);
     }
 }
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..52dcb16 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,7 @@
 import android.cts.util.TimeoutReq;
 import com.android.pts.util.MeasureRun;
 import com.android.pts.util.MeasureTime;
+import com.android.pts.util.PerfResultType;
 import com.android.pts.util.PtsAndroidTestCase;
 import com.android.pts.util.ReportLog;
 import com.android.pts.util.Stat;
@@ -65,7 +66,8 @@
                 mbps, true);
         getReportLog().printArray("Wr amount", wrAmount, true);
         Stat.StatResult stat = Stat.getStat(mbps);
-        getReportLog().printSummary("MB/s", stat.mAverage, stat.mStddev);
+        getReportLog().printSummary("MB/s", stat.mAverage, PerfResultType.HIGHER_BETTER,
+                stat.mStddev);
     }
 
     @TimeoutReq(minutes = 60)
@@ -106,6 +108,7 @@
         getReportLog().printArray("read MB/s",
                 mbps, true);
         Stat.StatResult stat = Stat.getStat(mbps);
-        getReportLog().printSummary("MB/s", stat.mAverage, stat.mStddev);
+        getReportLog().printSummary("MB/s", stat.mAverage, PerfResultType.HIGHER_BETTER,
+                stat.mStddev);
     }
 }
diff --git a/suite/pts/deviceTests/simplecpu/Android.mk b/suite/pts/deviceTests/simplecpu/Android.mk
new file mode 100644
index 0000000..988ddc3
--- /dev/null
+++ b/suite/pts/deviceTests/simplecpu/Android.mk
@@ -0,0 +1,35 @@
+# 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)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+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 := PtsDeviceSimpleCpu
+
+LOCAL_SDK_VERSION := 16
+
+include $(BUILD_PTS_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/deviceTests/simplecpu/jni/Android.mk b/suite/pts/deviceTests/simplecpu/jni/Android.mk
new file mode 100644
index 0000000..9c34e0e
--- /dev/null
+++ b/suite/pts/deviceTests/simplecpu/jni/Android.mk
@@ -0,0 +1,31 @@
+# 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_MODULE    := libptscpu_jni
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := CpuNativeJni.cpp
+
+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..ecf56e6
--- /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
+ */
+long currentTimeMillis()
+{
+    struct timeval tv;
+    gettimeofday(&tv, (struct timezone *) NULL);
+    return (long)tv.tv_sec * 1000 + tv.tv_usec / 1000;
+}
+
+/**
+ * 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 jlong 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;
+    }
+    long totalTime = 0;
+    for (int i = 0; i < repetition; i++) {
+        randomInitArray<int>(data, numberElements, 0);
+        long start = currentTimeMillis();
+        qsort_local(data, numberElements, sizeof(int), cmpint);
+        long 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 jlong 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;
+    }
+    long totalTime = 0;
+    for (int i = 0; i < repetition; i++) {
+        randomInitArray<float>(A, n * n, 0);
+        randomInitArray<float>(B, n * n, 1);
+        long start = currentTimeMillis();
+        doMatrixMultiplication(A, B, C, n);
+        long 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..666ff88
--- /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 spent in sorting in ms.
+     */
+    public static native long 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
+     */
+    public static native long 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..4231250
--- /dev/null
+++ b/suite/pts/deviceTests/simplecpu/src/com/android/pts/simplecpu/SimpleCpuTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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.PerfResultType;
+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("ms", result, false);
+        Stat.StatResult stat = Stat.getStat(result);
+        getReportLog().printSummary("ms", stat.mAverage, PerfResultType.LOWER_BETTER,
+                stat.mStddev);
+    }
+
+    /**
+     * 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("ms", result, false);
+        Stat.StatResult stat = Stat.getStat(result);
+        getReportLog().printSummary("ms", stat.mAverage, PerfResultType.LOWER_BETTER,
+                stat.mStddev);
+    }
+
+}
diff --git a/suite/pts/deviceTests/ui/Android.mk b/suite/pts/deviceTests/ui/Android.mk
index 28db8a1..731cbc7 100644
--- a/suite/pts/deviceTests/ui/Android.mk
+++ b/suite/pts/deviceTests/ui/Android.mk
@@ -24,7 +24,13 @@
 
 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
 
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..d6d5f06
--- /dev/null
+++ b/suite/pts/deviceTests/ui/src/com/android/pts/ui/FpsTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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.PerfResultType;
+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 ms", intervals, false);
+        getReportLog().printArray("jankiness ms", jankiness, false);
+        getReportLog().printSummaryFull("Frame interval",
+                "max delay ms", maxDelay, PerfResultType.LOWER_BETTER,
+                "number of jank", jankNumber, PerfResultType.LOWER_BETTER);
+    }
+}
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..5324669 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,7 @@
 import android.cts.util.TimeoutReq;
 import com.android.pts.util.MeasureRun;
 import com.android.pts.util.MeasureTime;
+import com.android.pts.util.PerfResultType;
 import com.android.pts.util.PtsActivityInstrumentationTestCase2;
 import com.android.pts.util.ReportLog;
 import com.android.pts.util.Stat;
@@ -67,6 +68,7 @@
         });
         getReportLog().printArray("ms", results, false);
         Stat.StatResult stat = Stat.getStat(results);
-        getReportLog().printSummary("Time ms", stat.mAverage, stat.mStddev);
+        getReportLog().printSummary("Time ms", stat.mAverage, PerfResultType.LOWER_BETTER,
+                stat.mStddev);
     }
 }
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..2e99bb8 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,7 @@
 import android.cts.util.TimeoutReq;
 import com.android.pts.util.MeasureRun;
 import com.android.pts.util.MeasureTime;
+import com.android.pts.util.PerfResultType;
 import com.android.pts.util.ReportLog;
 import com.android.pts.util.Stat;
 import com.android.pts.util.Stat.StatResult;
@@ -67,7 +68,7 @@
         });
         mReport.printArray("time in ms", result, false);
         StatResult stat = Stat.getStat(result);
-        mReport.printSummary("time in ms", stat.mAverage, stat.mStddev);
+        mReport.printSummary("time in ms", stat.mAverage, PerfResultType.LOWER_BETTER, stat.mStddev);
     }
 
     private void rebootDevice() throws DeviceNotAvailableException {
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
index b5400ab..54f2eb0 100644
--- a/suite/pts/hostTests/browser/src/com/android/pts/browser/BrowserTest.java
+++ b/suite/pts/hostTests/browser/src/com/android/pts/browser/BrowserTest.java
@@ -22,6 +22,7 @@
 import com.android.ddmlib.Log;
 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
 import com.android.pts.ptsutil.LogcatLineReceiver;
+import com.android.pts.util.PerfResultType;
 import com.android.pts.util.ReportLog;
 import com.android.pts.util.Stat;
 import com.android.tradefed.build.IBuildInfo;
@@ -98,7 +99,8 @@
         }
         mReport.printArray("scores", results, true);
         Stat.StatResult stat = Stat.getStat(results);
-        mReport.printSummary("Score", stat.mAverage, stat.mStddev);
+        // Octane score is higher-better
+        mReport.printSummary("Score", stat.mAverage, PerfResultType.HIGHER_BETTER, stat.mStddev);
     }
 
     private double runBenchmarking(String testMethodName, String resultPattern,
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..da9d8e2 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,7 @@
 
 import com.android.pts.util.MeasureRun;
 import com.android.pts.util.MeasureTime;
+import com.android.pts.util.PerfResultType;
 import com.android.pts.util.PtsAndroidTestCase;
 import com.android.pts.util.Stat;
 
@@ -83,7 +84,8 @@
         });
         getReportLog().printArray("ms", results, false);
         Stat.StatResult stat = Stat.getStat(results);
-        getReportLog().printSummary("Time ms", stat.mAverage, stat.mStddev);
+        getReportLog().printSummary("Time ms", stat.mAverage, PerfResultType.LOWER_BETTER,
+                stat.mStddev);
     }
 
     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..dedf19c 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
@@ -24,6 +24,7 @@
 import com.android.ddmlib.testrunner.TestIdentifier;
 import com.android.pts.util.MeasureRun;
 import com.android.pts.util.MeasureTime;
+import com.android.pts.util.PerfResultType;
 import com.android.pts.util.PtsException;
 import com.android.pts.util.ReportLog;
 import com.android.pts.util.Stat;
@@ -91,7 +92,8 @@
         });
         mReport.printArray("time in ms", result, false);
         StatResult stat = Stat.getStat(result);
-        mReport.printSummary("time in ms", stat.mAverage, stat.mStddev);
+        mReport.printSummary("time in ms", stat.mAverage, PerfResultType.LOWER_BETTER,
+                stat.mStddev);
     }
 
 }
diff --git a/suite/pts/lib/commonutil/src/com/android/pts/util/PerfResultType.java b/suite/pts/lib/commonutil/src/com/android/pts/util/PerfResultType.java
new file mode 100644
index 0000000..0d0f13d
--- /dev/null
+++ b/suite/pts/lib/commonutil/src/com/android/pts/util/PerfResultType.java
@@ -0,0 +1,28 @@
+/*
+ * 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 distinguish performance results.
+ * HIGHER_BETTER: better performance with higher value
+ * LOWER_BETTER: better performance with lower value
+ *
+ */
+public enum PerfResultType {
+    LOWER_BETTER,
+    HIGHER_BETTER;
+}
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..721a196 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
@@ -39,6 +39,7 @@
     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.
@@ -72,9 +73,33 @@
         printLog(builder.toString());
     }
 
-    public void printSummary(String header, double average, double stddev) {
-        mSummary = header + LOG_ELEM_SEPARATOR + "average " + average + LOG_ELEM_SEPARATOR +
-                "stddev " + stddev;
+    /**
+     * For standard report summary with average and stddev
+     * @param header
+     * @param average
+     * @param type type of average value. stddev does not need type.
+     * @param stddev
+     */
+    public void printSummary(String header, double average, PerfResultType type, double stddev) {
+        mSummary = header + LOG_ELEM_SEPARATOR + "average " + average + " " + type.ordinal() +
+                LOG_ELEM_SEPARATOR + "stddev " + stddev + " " +
+                PerfResultType.LOWER_BETTER.ordinal();
+    }
+
+    /**
+     * For a report with two representative values with custom key strings
+     * @param header
+     * @param key1 String key for val1
+     * @param val1
+     * @param val1type
+     * @param key2 String key for val2
+     * @param val2
+     * @param val2type
+     */
+    public void printSummaryFull(String header, String key1, double val1, PerfResultType val1type,
+            String key2, double val2, PerfResultType val2type) {
+        mSummary = header + LOG_ELEM_SEPARATOR + key1 + " " + val1 + " " + val1type.ordinal() +
+                LOG_ELEM_SEPARATOR + key2 + " " + val2 + " " + val2type.ordinal();
     }
 
     public void throwReportToHost() throws PtsException {
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..e1753e5 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
diff --git a/tests/res/layout/gridlayout_layout.xml b/tests/res/layout/gridlayout_layout.xml
new file mode 100644
index 0000000..54b3b2c
--- /dev/null
+++ b/tests/res/layout/gridlayout_layout.xml
@@ -0,0 +1,23 @@
+<?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.
+ -->
+<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/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/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/display/Android.mk b/tests/tests/display/Android.mk
new file mode 100644
index 0000000..ec5b40d
--- /dev/null
+++ b/tests/tests/display/Android.mk
@@ -0,0 +1,37 @@
+# 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)
+
+# 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 := ctstestrunner
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsDisplayTestCases
+
+LOCAL_SDK_VERSION := current
+
+# 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 $(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/tests/tests/dreams/Android.mk b/tests/tests/dreams/Android.mk
new file mode 100644
index 0000000..c630d8a
--- /dev/null
+++ b/tests/tests/dreams/Android.mk
@@ -0,0 +1,37 @@
+# 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_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 := ctstestrunner
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+# Need access to ServiceManager
+#LOCAL_SDK_VERSION := current
+
+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/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/tests/nativeopengl/libnativeopengltests/GLTestHelper.cpp b/tests/tests/nativeopengl/libnativeopengltests/GLTestHelper.cpp
new file mode 100644
index 0000000..ed527a6
--- /dev/null
+++ b/tests/tests/nativeopengl/libnativeopengltests/GLTestHelper.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "GLTestHelper.h"
+
+using namespace android;
+
+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/openglperf/Android.mk b/tests/tests/openglperf/Android.mk
index 2d1767e..548335b 100644
--- a/tests/tests/openglperf/Android.mk
+++ b/tests/tests/openglperf/Android.mk
@@ -25,6 +25,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
+LOCAL_JNI_SHARED_LIBRARIES := libctsopenglperf_jni
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsOpenGlPerfTestCases
@@ -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/tests/tests/openglperf/jni/Android.mk b/tests/tests/openglperf/jni/Android.mk
new file mode 100644
index 0000000..77db1df
--- /dev/null
+++ b/tests/tests/openglperf/jni/Android.mk
@@ -0,0 +1,31 @@
+# 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_MODULE    := libctsopenglperf_jni
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := OpenGlPerfNativeJni.cpp
+
+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..155ddeb 100644
--- a/tests/tests/openglperf/src/android/openglperf/cts/PlanetsRenderer.java
+++ b/tests/tests/openglperf/src/android/openglperf/cts/PlanetsRenderer.java
@@ -20,7 +20,6 @@
 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,13 +33,16 @@
 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;
@@ -65,9 +67,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;
@@ -104,6 +108,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 +128,7 @@
         measureTime("construction");
     }
 
+    @Override
     public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
         mProgram = createProgram(getVertexShader(), getFragmentShader());
         if (mProgram == 0) {
@@ -137,31 +145,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 +221,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..f64cc3f 100644
--- a/tests/tests/openglperf/src/android/openglperf/cts/PlanetsSurfaceView.java
+++ b/tests/tests/openglperf/src/android/openglperf/cts/PlanetsSurfaceView.java
@@ -17,9 +17,9 @@
 package android.openglperf.cts;
 
 import android.content.Context;
-import android.opengl.GLSurfaceView;
 
-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);
 
@@ -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/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..1c34087 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/AllocationTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/AllocationTest.java
@@ -188,10 +188,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);
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/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/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..7af4243 100644
--- a/tests/tests/security/src/android/security/cts/CertificateData.java
+++ b/tests/tests/security/src/android/security/cts/CertificateData.java
@@ -48,6 +48,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 +65,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 +100,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",
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/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/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..c4defca
--- /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 receivedRequest = false;
+        private final boolean accept;
+        private final boolean retain;
+
+        public TestSimpleGeolocationRequestWebChromeClient(
+                WebViewOnUiThread webViewOnUiThread, boolean accept, boolean retain) {
+            super(webViewOnUiThread);
+            this.accept = accept;
+            this.retain = retain;
+        }
+
+        @Override
+        public void onGeolocationPermissionsShowPrompt(
+                String origin, GeolocationPermissions.Callback callback) {
+            receivedRequest = true;
+            callback.invoke(origin, accept, retain);
+        }
+    }
+
+    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.receivedRequest;
+            }
+        };
+        PollingCheck.check("Geolocation prompt called", POLLING_TIMEOUT, receivedRequest);
+        Callable<Boolean> receivedLocation = new Callable<Boolean>() {
+            @Override
+            public Boolean call() {
+                return mJavascriptStatusReceiver.hasPosition;
+            }
+        };
+        PollingCheck.check("JS got position", POLLING_TIMEOUT, receivedLocation);
+        chromeClientAcceptOnce.receivedRequest = false;
+        // Load URL again, should receive callback again
+        loadUrlAndUpdateLocation(URL_1);
+        PollingCheck.check("Geolocation prompt called", POLLING_TIMEOUT, receivedRequest);
+        PollingCheck.check("JS got 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 received = false;
+        private final T expectedValue;
+        private T receivedValue = null;
+
+        public ValueCheck(T val) {
+            expectedValue = val;
+        }
+
+        @Override
+        protected boolean check() {
+            return received && expectedValue.equals(receivedValue);
+        }
+        @Override
+        public void onReceiveValue(T value) {
+            received = true;
+            receivedValue = 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.receivedRequest;
+            }
+        };
+        PollingCheck.check("Geolocation prompt called", POLLING_TIMEOUT, receivedRequest);
+        Callable<Boolean> receivedLocation = new Callable<Boolean>() {
+            @Override
+            public Boolean call() {
+                return mJavascriptStatusReceiver.hasPosition;
+            }
+        };
+        PollingCheck.check("JS got position", POLLING_TIMEOUT, receivedLocation);
+        chromeClientAcceptAlways.receivedRequest = false;
+        mJavascriptStatusReceiver.clearState();
+        // Load the same URL again
+        loadUrlAndUpdateLocation(URL_1);
+        PollingCheck.check("JS got position", POLLING_TIMEOUT, receivedLocation);
+        // Assert prompt for geolocation permission is not called the second time
+        assertFalse(chromeClientAcceptAlways.receivedRequest);
+        // 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.receivedRequest = false;
+        loadUrlAndUpdateLocation(URL_2);
+        // Checking the callback for geolocation permission prompt is called
+        PollingCheck.check("Geolocation prompt called", POLLING_TIMEOUT, receivedRequest);
+        PollingCheck.check("JS got 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.receivedRequest;
+            }
+        };
+        PollingCheck.check("Geolocation prompt called", POLLING_TIMEOUT, receivedRequest);
+        Callable<Boolean> locationDenied = new Callable<Boolean>() {
+            @Override
+            public Boolean call() {
+                return mJavascriptStatusReceiver.denied;
+            }
+        };
+        PollingCheck.check("JS got position", POLLING_TIMEOUT, locationDenied);
+        // Same result should happen on next run
+        mOnUiThread.loadUrlAndWaitForCompletion(URL_1);
+        PollingCheck.check("Geolocation prompt called", POLLING_TIMEOUT, receivedRequest);
+        PollingCheck.check("JS got 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 called", POLLING_TIMEOUT, receivedRequest);
+        PollingCheck.check("JS got position", POLLING_TIMEOUT, locationDenied);
+        // second load should now not get a prompt
+        chromeClientRejectAlways.receivedRequest = false;
+        mOnUiThread.loadUrlAndWaitForCompletion(URL_2);
+        PollingCheck.check("JS got position", POLLING_TIMEOUT, locationDenied);
+        PollingCheck.check("Geolocation prompt 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 hasPosition = false;
+        public volatile boolean denied = false;
+        public volatile boolean unavailable = false;
+        public volatile boolean timeout = false;
+
+        public void clearState() {
+            hasPosition = false;
+            denied = false;
+            unavailable = false;
+            timeout = false;
+        }
+
+        @JavascriptInterface
+        public void errorDenied() {
+            denied = true;
+        }
+
+        @JavascriptInterface
+        public void errorUnavailable() {
+            unavailable = true;
+        }
+
+        @JavascriptInterface
+        public void errorTimeout() {
+            timeout = true;
+        }
+
+        @JavascriptInterface
+        public void gotLocation() {
+            hasPosition = 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..42991d8 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
@@ -164,7 +164,7 @@
         assertEquals(1, mSettings.getUserAgent());
         mOnUiThread.loadUrlAndWaitForCompletion(url);
         String userAgent1 = mOnUiThread.getTitle();
-        assertNotNull(userAgent1);
+        assertEquals(userAgent1, mSettings.getUserAgentString());
 
         mSettings.setUserAgent(3);
         assertEquals(1, mSettings.getUserAgent());
@@ -175,7 +175,7 @@
         assertEquals(2, mSettings.getUserAgent());
         mOnUiThread.loadUrlAndWaitForCompletion(url);
         String userAgent2 = mOnUiThread.getTitle();
-        assertNotNull(userAgent2);
+        assertEquals(userAgent2, mSettings.getUserAgentString());
 
         mSettings.setUserAgent(3);
         assertEquals(2, mSettings.getUserAgent());
@@ -186,7 +186,7 @@
         assertEquals(0, mSettings.getUserAgent());
         mOnUiThread.loadUrlAndWaitForCompletion(url);
         String userAgent0 = mOnUiThread.getTitle();
-        assertNotNull(userAgent0);
+        assertEquals(userAgent0, mSettings.getUserAgentString());
 
         final String customUserAgent = "Cts/Test";
         mSettings.setUserAgentString(customUserAgent);
@@ -387,11 +387,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 +397,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 +409,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 +419,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() {
@@ -727,7 +718,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..07a54d1 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());
@@ -762,7 +774,6 @@
         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 +799,6 @@
 
     @UiThreadTest
     public void testLoadDataWithBaseUrl() throws Throwable {
-        assertNull(mWebView.getTitle());
         assertNull(mWebView.getUrl());
         String imgUrl = TestHtmlConstants.SMALL_IMG_URL; // relative
 
@@ -1024,40 +1034,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 +1451,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 +1490,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 +1676,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/uiautomator/Android.mk b/tests/uiautomator/Android.mk
new file mode 100644
index 0000000..b5bfae3
--- /dev/null
+++ b/tests/uiautomator/Android.mk
@@ -0,0 +1,34 @@
+# 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_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/local/tmp
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_MODULE := CtsUiAutomatorTests
+LOCAL_JAVA_LIBRARIES := uiautomator_sdk_v16
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_CTS_TEST_APK := CtsUiAutomatorApp
+LOCAL_CTS_TEST_APP_PACKAGE := com.android.cts.uiautomator
+LOCAL_CTS_TEST_PACKAGE := android.uiautomator
+
+include $(BUILD_CTS_UI_JAVA_LIBRARY)
+
+# Build the test APK using its own makefile, and any other CTS-related packages
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/uiautomator/test-apps/Android.mk b/tests/uiautomator/test-apps/Android.mk
new file mode 100644
index 0000000..e790e1e
--- /dev/null
+++ b/tests/uiautomator/test-apps/Android.mk
@@ -0,0 +1,15 @@
+# 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 $(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 73ab901..e3689f7 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/tools/cts-native-scanner/tests/Android.mk b/tools/cts-native-scanner/tests/Android.mk
new file mode 100644
index 0000000..6044b0d
--- /dev/null
+++ b/tools/cts-native-scanner/tests/Android.mk
@@ -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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Only compile source java files in this lib
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_MODULE := cts-native-scanner-tests
+LOCAL_MODULE_TAGS := optional
+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/res/report/cts_result.xsd b/tools/tradefed-host/res/report/cts_result.xsd
index 5e471d5..74eec37 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.14"
+           xmlns="http://compatibility.android.com/cts_result/1.14"
            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>
 
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/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));
+    }
+}