am 9dbc155b: (-s ours) am 71a8a437: am d6150a11: am cee87829: am 996b5190: am 35ed15aa: (-s ours) am 65c21107: am 327a5640: (-s ours) am 9ab46af7: DO NOT MERGE Cherry picked from https://googleplex-android-review.googlesource.com/322614

* commit '9dbc155b6fe2e329d91d5e9d4d2ada5f77af6963':
  DO NOT MERGE Cherry picked from https://googleplex-android-review.googlesource.com/322614
diff --git a/CtsBuild.mk b/CtsBuild.mk
index 6e57086..f6d0d5c 100644
--- a/CtsBuild.mk
+++ b/CtsBuild.mk
@@ -51,3 +51,7 @@
 define cts-get-test-xmls
 	$(foreach name,$(1),$(CTS_TESTCASES_OUT)/$(name).xml)
 endef
+
+define cts-get-executable-paths
+	$(foreach executable,$(1),$(CTS_TESTCASES_OUT)/$(executable))
+endef
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index dbf82c9..5fe6c5e 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -39,7 +39,6 @@
     CtsDeviceAdmin \
     CtsMonkeyApp \
     CtsMonkeyApp2 \
-    CtsSampleDeviceApp \
     CtsSomeAccessibilityServices \
     CtsTestStubs \
     SignatureTest \
@@ -103,11 +102,11 @@
     CtsPermission2TestCases \
     CtsPreferenceTestCases \
     CtsPreference2TestCases \
+    CtsPrintTestCases \
     CtsProviderTestCases \
     CtsRenderscriptTestCases \
     CtsRenderscriptGraphicsTestCases \
     CtsRsCppTestCases \
-    CtsSampleDeviceTestCases \
     CtsSaxTestCases \
     CtsSecurityTestCases \
     CtsSpeechTestCases \
@@ -132,20 +131,23 @@
     CtsAdbTests \
     CtsAppSecurityTests \
     CtsMonkeyTestCases \
-    CtsSampleHostTestCases \
     CtsUsbTests
 
 # Native test executables that need to have associated test XMLs.
 cts_native_exes := \
 	NativeMediaTest_SL \
 	NativeMediaTest_XA \
-	bionic-unit-tests-cts \
+	bionic-unit-tests-cts
 
 cts_ui_tests := \
     CtsUiAutomatorTests
 
 cts_device_jars := \
-    CtsDeviceJank
+    CtsDeviceJank \
+    CtsPrintInstrument
+
+cts_device_executables := \
+    print-instrument
 
 # All the files that will end up under the repository/testcases
 # directory of the final CTS distribution.
@@ -153,7 +155,8 @@
     $(call cts-get-package-paths,$(cts_test_packages)) \
     $(call cts-get-native-paths,$(cts_native_exes)) \
     $(call cts-get-ui-lib-paths,$(cts_ui_tests)) \
-    $(call cts-get-ui-lib-paths,$(cts_device_jars))
+    $(call cts-get-ui-lib-paths,$(cts_device_jars)) \
+    $(call cts-get-executable-paths,$(cts_device_executables))
 
 # All the XMLs that will end up under the repository/testcases
 # and that need to be created before making the final CTS distribution.
@@ -162,6 +165,5 @@
     $(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 :=
\ No newline at end of file
diff --git a/libs/testserver/src/android/webkit/cts/CtsTestServer.java b/libs/testserver/src/android/webkit/cts/CtsTestServer.java
index e4ac46e..a99459a 100644
--- a/libs/testserver/src/android/webkit/cts/CtsTestServer.java
+++ b/libs/testserver/src/android/webkit/cts/CtsTestServer.java
@@ -84,6 +84,7 @@
 import javax.net.ssl.KeyManager;
 import javax.net.ssl.KeyManagerFactory;
 import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocket;
 import javax.net.ssl.SSLSession;
 import javax.net.ssl.X509TrustManager;
 
@@ -124,6 +125,13 @@
     public static final String MESSAGE_403 = "403 forbidden";
     public static final String MESSAGE_404 = "404 not found";
 
+    public enum SslMode {
+        INSECURE,
+        NO_CLIENT_AUTH,
+        WANTS_CLIENT_AUTH,
+        NEEDS_CLIENT_AUTH,
+    }
+
     private static Hashtable<Integer, String> sReasons;
 
     private ServerThread mServerThread;
@@ -131,7 +139,7 @@
     private AssetManager mAssets;
     private Context mContext;
     private Resources mResources;
-    private boolean mSsl;
+    private SslMode mSsl;
     private MimeTypeMap mMap;
     private Vector<String> mQueries;
     private ArrayList<HttpEntity> mRequestEntities;
@@ -166,19 +174,30 @@
      * @throws Exception
      */
     public CtsTestServer(Context context, boolean ssl) throws Exception {
+        this(context, ssl ? SslMode.NO_CLIENT_AUTH : SslMode.INSECURE);
+    }
+
+    /**
+     * Create and start a local HTTP server instance.
+     * @param context The application context to use for fetching assets.
+     * @param sslMode Whether to use SSL, and if so, what client auth (if any) to use.
+     * @throws Exception
+     */
+    public CtsTestServer(Context context, SslMode sslMode) throws Exception {
         mContext = context;
         mAssets = mContext.getAssets();
         mResources = mContext.getResources();
-        mSsl = ssl;
+        mSsl = sslMode;
         mRequestEntities = new ArrayList<HttpEntity>();
         mMap = MimeTypeMap.getSingleton();
         mQueries = new Vector<String>();
         mServerThread = new ServerThread(this, mSsl);
-        if (mSsl) {
-            mServerUri = "https://localhost:" + mServerThread.mSocket.getLocalPort();
+        if (mSsl == SslMode.INSECURE) {
+            mServerUri = "http:";
         } else {
-            mServerUri = "http://localhost:" + mServerThread.mSocket.getLocalPort();
+            mServerUri = "https:";
         }
+        mServerUri += "//localhost:" + mServerThread.mSocket.getLocalPort();
         mServerThread.start();
     }
 
@@ -217,7 +236,9 @@
 
     private URLConnection openConnection(URL url)
             throws IOException, NoSuchAlgorithmException, KeyManagementException {
-        if (mSsl) {
+        if (mSsl == SslMode.INSECURE) {
+            return url.openConnection();
+        } else {
             // Install hostname verifiers and trust managers that don't do
             // anything in order to get around the client not trusting
             // the test server due to a lack of certificates.
@@ -226,13 +247,14 @@
             connection.setHostnameVerifier(new CtsHostnameVerifier());
 
             SSLContext context = SSLContext.getInstance("TLS");
-            CtsTrustManager trustManager = new CtsTrustManager();
-            context.init(null, new CtsTrustManager[] {trustManager}, null);
+            try {
+                context.init(ServerThread.getKeyManagers(), getTrustManagers(), null);
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
             connection.setSSLSocketFactory(context.getSocketFactory());
 
             return connection;
-        } else {
-            return url.openConnection();
         }
     }
 
@@ -244,7 +266,7 @@
      */
     private static class CtsTrustManager implements X509TrustManager {
         public void checkClientTrusted(X509Certificate[] chain, String authType) {
-            // Trust the CtSTestServer...
+            // Trust the CtSTestServer's client...
         }
 
         public void checkServerTrusted(X509Certificate[] chain, String authType) {
@@ -257,6 +279,13 @@
     }
 
     /**
+     * @returns a trust manager array configured to permit any trust decision.
+     */
+    private static CtsTrustManager[] getTrustManagers() {
+        return new CtsTrustManager[] { new CtsTrustManager() };
+    }
+
+    /**
      * {@link HostnameVerifier} that verifies everybody. This permits
      * the client to trust the web server and call
      * {@link CtsTestServer#shutdown()}.
@@ -541,6 +570,8 @@
                     response = createResponse(HttpStatus.SC_OK);
                     response.setEntity(entity);
                     response.addHeader("Content-Disposition", "attachment; filename=test.bin");
+                    response.addHeader("Content-Type", mimeType);
+                    response.addHeader("Content-Length", "" + length);
                 } else {
                     // fall through, return 404 at the end
                 }
@@ -767,7 +798,7 @@
     private static class ServerThread extends Thread {
         private CtsTestServer mServer;
         private ServerSocket mSocket;
-        private boolean mIsSsl;
+        private SslMode mSsl;
         private boolean mIsCancelled;
         private SSLContext mSslContext;
         private ExecutorService mExecutorService = Executors.newFixedThreadPool(20);
@@ -802,13 +833,13 @@
             "1gaEjsC/0wGmmBDg1dTDH+F1p9TInzr3EFuYD0YiQ7YlAHq3cPuyGoLXJ5dXYuSBfhDXJSeddUkl" +
             "k1ufZyOOcskeInQge7jzaRfmKg3U94r+spMEvb0AzDQVOKvjjo1ivxMSgFRZaDb/4qw=";
 
-        private String PASSWORD = "android";
+        private static final String PASSWORD = "android";
 
         /**
          * Loads a keystore from a base64-encoded String. Returns the KeyManager[]
          * for the result.
          */
-        private KeyManager[] getKeyManagers() throws Exception {
+        private static KeyManager[] getKeyManagers() throws Exception {
             byte[] bytes = Base64.decode(SERVER_KEYS_BKS.getBytes());
             InputStream inputStream = new ByteArrayInputStream(bytes);
 
@@ -824,19 +855,24 @@
         }
 
 
-        public ServerThread(CtsTestServer server, boolean ssl) throws Exception {
+        public ServerThread(CtsTestServer server, SslMode sslMode) throws Exception {
             super("ServerThread");
             mServer = server;
-            mIsSsl = ssl;
+            mSsl = sslMode;
             int retry = 3;
             while (true) {
                 try {
-                    if (mIsSsl) {
-                        mSslContext = SSLContext.getInstance("TLS");
-                        mSslContext.init(getKeyManagers(), null, null);
-                        mSocket = mSslContext.getServerSocketFactory().createServerSocket(0);
-                    } else {
+                    if (mSsl == SslMode.INSECURE) {
                         mSocket = new ServerSocket(0);
+                    } else {  // Use SSL
+                        mSslContext = SSLContext.getInstance("TLS");
+                        mSslContext.init(getKeyManagers(), getTrustManagers(), null);
+                        mSocket = mSslContext.getServerSocketFactory().createServerSocket(0);
+                        if (mSsl == SslMode.WANTS_CLIENT_AUTH) {
+                            ((SSLServerSocket) mSocket).setWantClientAuth(true);
+                        } else if (mSsl == SslMode.NEEDS_CLIENT_AUTH) {
+                            ((SSLServerSocket) mSocket).setNeedClientAuth(true);
+                        }
                     }
                     return;
                 } catch (IOException e) {
diff --git a/suite/audio_quality/test_description/dut_playback_sample.xml b/suite/audio_quality/test_description/dut_playback_sample.xml
new file mode 100644
index 0000000..f78209e
--- /dev/null
+++ b/suite/audio_quality/test_description/dut_playback_sample.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright (C) 2014 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.
+-->
+
+<case name="dut_playback_sample" version="1.0" description="Sample test which check frequency of DUT's playback">
+	<setup>
+		<!-- prepare sound source id: to be used in output, sine 1000Hz, 4000ms long -->
+		<sound id="sound1" type="sin:32000:1000:4000" preload="1" />
+	</setup>
+	<action>
+		<sequential repeat="1" index="i">
+			<output device="DUT" id="sound1" gain="100" sync="start" waitforcompletion="0" />
+			<sequential repeat="1" index="j">
+				<!-- dummy recording to compensate for possible playback latency -->
+				<input device="host" id="dummy" gain="100" time="1000" sync="complete" />
+				<input device="host" id="host_in_$j" gain="100" time="2000" sync="complete" />
+			</sequential>
+		</sequential>
+		<sequential repeat="1" index="k">
+			<!-- input: host record, signal frequency in Hz, threshold, output: frequency calculated -->
+			<process method="script:playback_sample" input="id:host_in_$k,consti:1000,constf:5.0" output="val:freq_device_$k" />
+		</sequential>
+	</action>
+	<save file="host_in_.*" report="freq_device_.*" />
+</case>
diff --git a/suite/audio_quality/test_description/processing/playback_sample.py b/suite/audio_quality/test_description/processing/playback_sample.py
new file mode 100644
index 0000000..79e8d53
--- /dev/null
+++ b/suite/audio_quality/test_description/processing/playback_sample.py
@@ -0,0 +1,63 @@
+#!/usr/bin/python
+
+# Copyright (C) 2014 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.
+
+from consts import *
+
+# Sample test for dut_playback_sample case
+# Input: host recording (mono),
+#        frequency of sine in Hz (i64)
+#        pass level threshold (double)
+# Output: device (double) frequency
+
+def playback_sample(inputData, inputTypes):
+    output = []
+    outputData = []
+    outputTypes = []
+    # basic sanity check
+    inputError = False
+    if (inputTypes[0] != TYPE_MONO):
+        inputError = True
+    if (inputTypes[1] != TYPE_I64):
+        inputError = True
+    if (inputTypes[2] != TYPE_DOUBLE):
+        inputError = True
+    if inputError:
+        output.append(RESULT_ERROR)
+        output.append(outputData)
+        output.append(outputTypes)
+        return output
+
+    hostRecording = inputData[0]
+    signalFrequency = inputData[1]
+    threshold = inputData[2]
+    samplingRate = 44100
+
+    freq = calc_freq(hostRecording, samplingRate)
+    print "Expected Freq ", signalFrequency, "Actual Freq ", freq, "Threshold % ", threshold
+    diff = abs(freq - signalFrequency)
+    if (diff < threshold):
+        output.append(RESULT_PASS)
+    else:
+        output.append(RESULT_OK)
+    outputData.append(freq)
+    outputTypes.append(TYPE_DOUBLE)
+    output.append(outputData)
+    output.append(outputTypes)
+    return output
+
+def calc_freq(recording, samplingRate):
+    #This would calculate the frequency of recording, but is skipped in this sample test for brevity
+    return 32000
\ No newline at end of file
diff --git a/suite/cts/deviceTests/opengl/src/com/android/cts/opengl/primitive/GLPrimitiveBenchmark.java b/suite/cts/deviceTests/opengl/src/com/android/cts/opengl/primitive/GLPrimitiveBenchmark.java
index 6c2c87d..c177129 100644
--- a/suite/cts/deviceTests/opengl/src/com/android/cts/opengl/primitive/GLPrimitiveBenchmark.java
+++ b/suite/cts/deviceTests/opengl/src/com/android/cts/opengl/primitive/GLPrimitiveBenchmark.java
@@ -118,9 +118,8 @@
         intent.putExtra(GLActivityIntentKeys.INTENT_EXTRA_NUM_ITERATIONS, numIterations);
         intent.putExtra(GLActivityIntentKeys.INTENT_EXTRA_TIMEOUT, timeout);
 
-        GLPrimitiveActivity activity = null;
         setActivityIntent(intent);
-        activity = getActivity();
+        GLPrimitiveActivity activity = getActivity();
         if (activity != null) {
             activity.waitForCompletion();
             double[] fpsValues = activity.mFpsValues;
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index b65ded0..cc5832c 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -583,6 +583,12 @@
             android:configChanges="keyboardHidden|orientation|screenSize">
         </activity>
 
+        <activity android:name="android.hardware.camera2.cts.Camera2SurfaceViewStubActivity"
+            android:label="Camera2StubActivity"
+            android:screenOrientation="landscape"
+            android:configChanges="keyboardHidden|orientation|screenSize">
+        </activity>
+
         <activity android:name="android.view.inputmethod.cts.InputMethodStubActivity"
             android:label="InputMethodStubActivity">
             <intent-filter>
diff --git a/tests/SignatureTest/Android.mk b/tests/SignatureTest/Android.mk
index 209e78a..696f99e 100644
--- a/tests/SignatureTest/Android.mk
+++ b/tests/SignatureTest/Android.mk
@@ -44,8 +44,6 @@
 
 include $(BUILD_CTS_PACKAGE)
 
-$(info $(call local-intermediates-dir))
-
 generated_res_stamp := $(intermediates.COMMON)/genres.stamp
 api_ver_file := $(intermediates.COMMON)/api_ver_is_$(CTS_API_VERSION)
 
diff --git a/tests/SignatureTest/src/android/tests/sigtest/JDiffClassDescription.java b/tests/SignatureTest/src/android/tests/sigtest/JDiffClassDescription.java
index c51c6c3..36360d6 100644
--- a/tests/SignatureTest/src/android/tests/sigtest/JDiffClassDescription.java
+++ b/tests/SignatureTest/src/android/tests/sigtest/JDiffClassDescription.java
@@ -832,19 +832,14 @@
         // Nothing to check if it doesn't extend anything.
         if (mExtendedClass != null) {
             Class<?> superClass = mClass.getSuperclass();
-            if (superClass == null) {
-                // API indicates superclass, reflection doesn't.
-                return false;
-            }
 
-            if (superClass.getCanonicalName().equals(mExtendedClass)) {
-                return true;
+            while (superClass != null) {
+                if (superClass.getCanonicalName().equals(mExtendedClass)) {
+                    return true;
+                }
+                superClass = superClass.getSuperclass();
             }
-
-            if (mAbsoluteClassName.equals("android.hardware.SensorManager")) {
-                // FIXME: Please see Issue 1496822 for more information
-                return true;
-            }
+            // Couldn't find a matching superclass.
             return false;
         }
         return true;
diff --git a/tests/accessibility/AndroidManifest.xml b/tests/accessibility/AndroidManifest.xml
index 0d18cef..dde1de8 100644
--- a/tests/accessibility/AndroidManifest.xml
+++ b/tests/accessibility/AndroidManifest.xml
@@ -19,8 +19,6 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="android.view.accessibility.services">
 
-  <uses-permission android:name="android.permission.CAN_REQUEST_TOUCH_EXPLORATION_MODE"/>
-
   <application>
 
     <service android:name=".SpeakingAccessibilityService"
diff --git a/tests/assets/webkit/iframe_blank_tag.html b/tests/assets/webkit/iframe_blank_tag.html
index 55ff410..fbc6dc6 100644
--- a/tests/assets/webkit/iframe_blank_tag.html
+++ b/tests/assets/webkit/iframe_blank_tag.html
@@ -16,7 +16,7 @@
 <!DOCTYPE html>
 <html>
 <head>
-  <script type='text/javascript'> window.open('test_hello_world.html'); </script>
+  <script type='text/javascript'> window.open('page_with_link.html'); </script>
 </head>
 </html>
 
diff --git a/tests/assets/webkit/page_with_link.html b/tests/assets/webkit/page_with_link.html
new file mode 100644
index 0000000..50fb78a
--- /dev/null
+++ b/tests/assets/webkit/page_with_link.html
@@ -0,0 +1,20 @@
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<html>
+  <body>
+    <a href="http://foo.com" id="link">a link</a>
+  </body>
+</html>
diff --git a/tests/core/libcore/com/Android.mk b/tests/core/libcore/com/Android.mk
index 02dc3de..db08dbd 100644
--- a/tests/core/libcore/com/Android.mk
+++ b/tests/core/libcore/com/Android.mk
@@ -14,10 +14,6 @@
 
 LOCAL_PATH:= $(call my-dir)
 
-ifeq ($(BUILD_CTSCORE_PACKAGE),)
-    $(error BUILD_CTSCORE_PACKAGE must be defined)
-endif
-
 include $(CLEAR_VARS)
 LOCAL_PACKAGE_NAME := android.core.tests.libcore.package.com
 LOCAL_STATIC_JAVA_LIBRARIES := core-tests
diff --git a/tests/core/libcore/dalvik/Android.mk b/tests/core/libcore/dalvik/Android.mk
index 7b77a75..42d14f3 100644
--- a/tests/core/libcore/dalvik/Android.mk
+++ b/tests/core/libcore/dalvik/Android.mk
@@ -14,10 +14,6 @@
 
 LOCAL_PATH:= $(call my-dir)
 
-ifeq ($(BUILD_CTSCORE_PACKAGE),)
-    $(error BUILD_CTSCORE_PACKAGE must be defined)
-endif
-
 include $(CLEAR_VARS)
 LOCAL_PACKAGE_NAME := android.core.tests.libcore.package.dalvik
 LOCAL_STATIC_JAVA_LIBRARIES := core-tests
diff --git a/tests/core/libcore/harmony_annotation/Android.mk b/tests/core/libcore/harmony_annotation/Android.mk
new file mode 100644
index 0000000..e9f716e
--- /dev/null
+++ b/tests/core/libcore/harmony_annotation/Android.mk
@@ -0,0 +1,24 @@
+# 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)
+
+ifeq ($(BUILD_CTSCORE_PACKAGE),)
+    $(error BUILD_CTSCORE_PACKAGE must be defined)
+endif
+
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := android.core.tests.libcore.package.harmony_annotation
+LOCAL_STATIC_JAVA_LIBRARIES := core-tests
+include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/libcore/harmony_annotation/AndroidManifest.xml b/tests/core/libcore/harmony_annotation/AndroidManifest.xml
new file mode 100644
index 0000000..0c59b1b
--- /dev/null
+++ b/tests/core/libcore/harmony_annotation/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="android.core.tests.libcore.package.harmony_annotation">
+    <uses-permission android:name="android.permission.INTERNET" />
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+                     android:targetPackage="android.core.tests.runner"
+                     android:label="cts framework tests"/>
+
+</manifest>
diff --git a/tests/core/libcore/harmony_beans/Android.mk b/tests/core/libcore/harmony_beans/Android.mk
new file mode 100644
index 0000000..2131ae0
--- /dev/null
+++ b/tests/core/libcore/harmony_beans/Android.mk
@@ -0,0 +1,24 @@
+# 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)
+
+ifeq ($(BUILD_CTSCORE_PACKAGE),)
+    $(error BUILD_CTSCORE_PACKAGE must be defined)
+endif
+
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := android.core.tests.libcore.package.harmony_beans
+LOCAL_STATIC_JAVA_LIBRARIES := apache-harmony-tests
+include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/libcore/harmony_beans/AndroidManifest.xml b/tests/core/libcore/harmony_beans/AndroidManifest.xml
new file mode 100644
index 0000000..b4932dd
--- /dev/null
+++ b/tests/core/libcore/harmony_beans/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="android.core.tests.libcore.package.harmony_beans">
+    <uses-permission android:name="android.permission.INTERNET" />
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+                     android:targetPackage="android.core.tests.runner"
+                     android:label="cts framework tests"/>
+
+</manifest>
diff --git a/tests/core/libcore/harmony_java_io/Android.mk b/tests/core/libcore/harmony_java_io/Android.mk
new file mode 100644
index 0000000..a8d4fa0
--- /dev/null
+++ b/tests/core/libcore/harmony_java_io/Android.mk
@@ -0,0 +1,24 @@
+# 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)
+
+ifeq ($(BUILD_CTSCORE_PACKAGE),)
+    $(error BUILD_CTSCORE_PACKAGE must be defined)
+endif
+
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := android.core.tests.libcore.package.harmony_java_io
+LOCAL_STATIC_JAVA_LIBRARIES := core-tests
+include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/libcore/harmony_java_io/AndroidManifest.xml b/tests/core/libcore/harmony_java_io/AndroidManifest.xml
new file mode 100644
index 0000000..65d64ab
--- /dev/null
+++ b/tests/core/libcore/harmony_java_io/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="android.core.tests.libcore.package.harmony_java_io">
+    <uses-permission android:name="android.permission.INTERNET" />
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+                     android:targetPackage="android.core.tests.runner"
+                     android:label="cts framework tests"/>
+
+</manifest>
diff --git a/tests/core/libcore/harmony_java_lang/Android.mk b/tests/core/libcore/harmony_java_lang/Android.mk
new file mode 100644
index 0000000..8b1bdff
--- /dev/null
+++ b/tests/core/libcore/harmony_java_lang/Android.mk
@@ -0,0 +1,24 @@
+# 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)
+
+ifeq ($(BUILD_CTSCORE_PACKAGE),)
+    $(error BUILD_CTSCORE_PACKAGE must be defined)
+endif
+
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := android.core.tests.libcore.package.harmony_java_lang
+LOCAL_STATIC_JAVA_LIBRARIES := core-tests
+include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/libcore/harmony_java_lang/AndroidManifest.xml b/tests/core/libcore/harmony_java_lang/AndroidManifest.xml
new file mode 100644
index 0000000..a5e499a
--- /dev/null
+++ b/tests/core/libcore/harmony_java_lang/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="android.core.tests.libcore.package.harmony_java_lang">
+    <uses-permission android:name="android.permission.INTERNET" />
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+                     android:targetPackage="android.core.tests.runner"
+                     android:label="cts framework tests"/>
+
+</manifest>
diff --git a/tests/core/libcore/harmony_java_math/Android.mk b/tests/core/libcore/harmony_java_math/Android.mk
new file mode 100644
index 0000000..8310743
--- /dev/null
+++ b/tests/core/libcore/harmony_java_math/Android.mk
@@ -0,0 +1,24 @@
+# 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)
+
+ifeq ($(BUILD_CTSCORE_PACKAGE),)
+    $(error BUILD_CTSCORE_PACKAGE must be defined)
+endif
+
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := android.core.tests.libcore.package.harmony_java_math
+LOCAL_STATIC_JAVA_LIBRARIES := core-tests
+include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/libcore/harmony_java_math/AndroidManifest.xml b/tests/core/libcore/harmony_java_math/AndroidManifest.xml
new file mode 100644
index 0000000..f8cd224
--- /dev/null
+++ b/tests/core/libcore/harmony_java_math/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="android.core.tests.libcore.package.harmony_java_math">
+    <uses-permission android:name="android.permission.INTERNET" />
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+                     android:targetPackage="android.core.tests.runner"
+                     android:label="cts framework tests"/>
+
+</manifest>
diff --git a/tests/core/libcore/harmony_java_net/Android.mk b/tests/core/libcore/harmony_java_net/Android.mk
new file mode 100644
index 0000000..7917bcc
--- /dev/null
+++ b/tests/core/libcore/harmony_java_net/Android.mk
@@ -0,0 +1,24 @@
+# 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)
+
+ifeq ($(BUILD_CTSCORE_PACKAGE),)
+    $(error BUILD_CTSCORE_PACKAGE must be defined)
+endif
+
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := android.core.tests.libcore.package.harmony_java_net
+LOCAL_STATIC_JAVA_LIBRARIES := core-tests
+include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/libcore/harmony_java_net/AndroidManifest.xml b/tests/core/libcore/harmony_java_net/AndroidManifest.xml
new file mode 100644
index 0000000..3c9fd63
--- /dev/null
+++ b/tests/core/libcore/harmony_java_net/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="android.core.tests.libcore.package.harmony_java_net">
+    <uses-permission android:name="android.permission.INTERNET" />
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+                     android:targetPackage="android.core.tests.runner"
+                     android:label="cts framework tests"/>
+
+</manifest>
diff --git a/tests/core/libcore/harmony_java_nio/Android.mk b/tests/core/libcore/harmony_java_nio/Android.mk
new file mode 100644
index 0000000..2c6f673
--- /dev/null
+++ b/tests/core/libcore/harmony_java_nio/Android.mk
@@ -0,0 +1,24 @@
+# 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)
+
+ifeq ($(BUILD_CTSCORE_PACKAGE),)
+    $(error BUILD_CTSCORE_PACKAGE must be defined)
+endif
+
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := android.core.tests.libcore.package.harmony_java_nio
+LOCAL_STATIC_JAVA_LIBRARIES := core-tests
+include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/libcore/harmony_java_nio/AndroidManifest.xml b/tests/core/libcore/harmony_java_nio/AndroidManifest.xml
new file mode 100644
index 0000000..27166d4
--- /dev/null
+++ b/tests/core/libcore/harmony_java_nio/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="android.core.tests.libcore.package.harmony_java_nio">
+    <uses-permission android:name="android.permission.INTERNET" />
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+                     android:targetPackage="android.core.tests.runner"
+                     android:label="cts framework tests"/>
+
+</manifest>
diff --git a/tests/core/libcore/harmony_java_text/Android.mk b/tests/core/libcore/harmony_java_text/Android.mk
new file mode 100644
index 0000000..ecd1574
--- /dev/null
+++ b/tests/core/libcore/harmony_java_text/Android.mk
@@ -0,0 +1,24 @@
+# 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)
+
+ifeq ($(BUILD_CTSCORE_PACKAGE),)
+    $(error BUILD_CTSCORE_PACKAGE must be defined)
+endif
+
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := android.core.tests.libcore.package.harmony_java_text
+LOCAL_STATIC_JAVA_LIBRARIES := core-tests
+include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/libcore/harmony_java_text/AndroidManifest.xml b/tests/core/libcore/harmony_java_text/AndroidManifest.xml
new file mode 100644
index 0000000..0b6beed
--- /dev/null
+++ b/tests/core/libcore/harmony_java_text/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="android.core.tests.libcore.package.harmony_java_text">
+    <uses-permission android:name="android.permission.INTERNET" />
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+                     android:targetPackage="android.core.tests.runner"
+                     android:label="cts framework tests"/>
+
+</manifest>
diff --git a/tests/core/libcore/harmony_java_util/Android.mk b/tests/core/libcore/harmony_java_util/Android.mk
new file mode 100644
index 0000000..6d7bded
--- /dev/null
+++ b/tests/core/libcore/harmony_java_util/Android.mk
@@ -0,0 +1,24 @@
+# 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)
+
+ifeq ($(BUILD_CTSCORE_PACKAGE),)
+    $(error BUILD_CTSCORE_PACKAGE must be defined)
+endif
+
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := android.core.tests.libcore.package.harmony_java_util
+LOCAL_STATIC_JAVA_LIBRARIES := core-tests
+include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/libcore/harmony_java_util/AndroidManifest.xml b/tests/core/libcore/harmony_java_util/AndroidManifest.xml
new file mode 100644
index 0000000..72fa3ef
--- /dev/null
+++ b/tests/core/libcore/harmony_java_util/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="android.core.tests.libcore.package.harmony_java_util">
+    <uses-permission android:name="android.permission.INTERNET" />
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+                     android:targetPackage="android.core.tests.runner"
+                     android:label="cts framework tests"/>
+
+</manifest>
diff --git a/tests/core/libcore/harmony_javax_security/Android.mk b/tests/core/libcore/harmony_javax_security/Android.mk
new file mode 100644
index 0000000..011940d
--- /dev/null
+++ b/tests/core/libcore/harmony_javax_security/Android.mk
@@ -0,0 +1,24 @@
+# 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)
+
+ifeq ($(BUILD_CTSCORE_PACKAGE),)
+    $(error BUILD_CTSCORE_PACKAGE must be defined)
+endif
+
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := android.core.tests.libcore.package.harmony_javax_security
+LOCAL_STATIC_JAVA_LIBRARIES := core-tests
+include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/libcore/harmony_javax_security/AndroidManifest.xml b/tests/core/libcore/harmony_javax_security/AndroidManifest.xml
new file mode 100644
index 0000000..b7b35f2
--- /dev/null
+++ b/tests/core/libcore/harmony_javax_security/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="android.core.tests.libcore.package.harmony_javax_security">
+    <uses-permission android:name="android.permission.INTERNET" />
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+                     android:targetPackage="android.core.tests.runner"
+                     android:label="cts framework tests"/>
+
+</manifest>
diff --git a/tests/core/libcore/harmony_logging/Android.mk b/tests/core/libcore/harmony_logging/Android.mk
new file mode 100644
index 0000000..2ec10f1
--- /dev/null
+++ b/tests/core/libcore/harmony_logging/Android.mk
@@ -0,0 +1,24 @@
+# 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)
+
+ifeq ($(BUILD_CTSCORE_PACKAGE),)
+    $(error BUILD_CTSCORE_PACKAGE must be defined)
+endif
+
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := android.core.tests.libcore.package.harmony_logging
+LOCAL_STATIC_JAVA_LIBRARIES := apache-harmony-tests
+include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/libcore/harmony_logging/AndroidManifest.xml b/tests/core/libcore/harmony_logging/AndroidManifest.xml
new file mode 100644
index 0000000..94ee60e
--- /dev/null
+++ b/tests/core/libcore/harmony_logging/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="android.core.tests.libcore.package.harmony_logging">
+    <uses-permission android:name="android.permission.INTERNET" />
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+                     android:targetPackage="android.core.tests.runner"
+                     android:label="cts framework tests"/>
+
+</manifest>
diff --git a/tests/core/libcore/harmony_prefs/Android.mk b/tests/core/libcore/harmony_prefs/Android.mk
new file mode 100644
index 0000000..92b0c7d
--- /dev/null
+++ b/tests/core/libcore/harmony_prefs/Android.mk
@@ -0,0 +1,24 @@
+# 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)
+
+ifeq ($(BUILD_CTSCORE_PACKAGE),)
+    $(error BUILD_CTSCORE_PACKAGE must be defined)
+endif
+
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := android.core.tests.libcore.package.harmony_prefs
+LOCAL_STATIC_JAVA_LIBRARIES := apache-harmony-tests
+include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/libcore/harmony_prefs/AndroidManifest.xml b/tests/core/libcore/harmony_prefs/AndroidManifest.xml
new file mode 100644
index 0000000..f8fdea2
--- /dev/null
+++ b/tests/core/libcore/harmony_prefs/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="android.core.tests.libcore.package.harmony_prefs">
+    <uses-permission android:name="android.permission.INTERNET" />
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+                     android:targetPackage="android.core.tests.runner"
+                     android:label="cts framework tests"/>
+
+</manifest>
diff --git a/tests/core/libcore/harmony_sql/Android.mk b/tests/core/libcore/harmony_sql/Android.mk
new file mode 100644
index 0000000..b1df215
--- /dev/null
+++ b/tests/core/libcore/harmony_sql/Android.mk
@@ -0,0 +1,24 @@
+# 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)
+
+ifeq ($(BUILD_CTSCORE_PACKAGE),)
+    $(error BUILD_CTSCORE_PACKAGE must be defined)
+endif
+
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := android.core.tests.libcore.package.harmony_sql
+LOCAL_STATIC_JAVA_LIBRARIES := apache-harmony-tests
+include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/libcore/harmony_sql/AndroidManifest.xml b/tests/core/libcore/harmony_sql/AndroidManifest.xml
new file mode 100644
index 0000000..c6c31b2
--- /dev/null
+++ b/tests/core/libcore/harmony_sql/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="android.core.tests.libcore.package.harmony_sql">
+    <uses-permission android:name="android.permission.INTERNET" />
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+                     android:targetPackage="android.core.tests.runner"
+                     android:label="cts framework tests"/>
+
+</manifest>
diff --git a/tests/core/libcore/jsr166/Android.mk b/tests/core/libcore/jsr166/Android.mk
new file mode 100644
index 0000000..3f9871e
--- /dev/null
+++ b/tests/core/libcore/jsr166/Android.mk
@@ -0,0 +1,20 @@
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := android.core.tests.libcore.package.jsr166
+LOCAL_STATIC_JAVA_LIBRARIES := jsr166-tests
+include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/libcore/jsr166/AndroidManifest.xml b/tests/core/libcore/jsr166/AndroidManifest.xml
new file mode 100644
index 0000000..3a0150e
--- /dev/null
+++ b/tests/core/libcore/jsr166/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.core.tests.libcore.package.jsr166">
+    <uses-permission android:name="android.permission.INTERNET" />
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+                     android:targetPackage="android.core.tests.runner"
+                     android:label="cts framework tests"/>
+
+</manifest>
diff --git a/tests/core/libcore/libcore/Android.mk b/tests/core/libcore/libcore/Android.mk
index 382b386..a86d2c0 100644
--- a/tests/core/libcore/libcore/Android.mk
+++ b/tests/core/libcore/libcore/Android.mk
@@ -14,10 +14,6 @@
 
 LOCAL_PATH:= $(call my-dir)
 
-ifeq ($(BUILD_CTSCORE_PACKAGE),)
-    $(error BUILD_CTSCORE_PACKAGE must be defined)
-endif
-
 include $(CLEAR_VARS)
 LOCAL_PACKAGE_NAME := android.core.tests.libcore.package.libcore
 LOCAL_STATIC_JAVA_LIBRARIES := core-tests
diff --git a/tests/core/libcore/org/Android.mk b/tests/core/libcore/org/Android.mk
index d7a96b3..0f3f0ca 100644
--- a/tests/core/libcore/org/Android.mk
+++ b/tests/core/libcore/org/Android.mk
@@ -14,10 +14,6 @@
 
 LOCAL_PATH:= $(call my-dir)
 
-ifeq ($(BUILD_CTSCORE_PACKAGE),)
-    $(error BUILD_CTSCORE_PACKAGE must be defined)
-endif
-
 include $(CLEAR_VARS)
 LOCAL_PACKAGE_NAME := android.core.tests.libcore.package.org
 LOCAL_STATIC_JAVA_LIBRARIES := core-tests
diff --git a/tests/core/libcore/sun/Android.mk b/tests/core/libcore/sun/Android.mk
index 44d3d70..ed6d2c7 100644
--- a/tests/core/libcore/sun/Android.mk
+++ b/tests/core/libcore/sun/Android.mk
@@ -14,10 +14,6 @@
 
 LOCAL_PATH:= $(call my-dir)
 
-ifeq ($(BUILD_CTSCORE_PACKAGE),)
-    $(error BUILD_CTSCORE_PACKAGE must be defined)
-endif
-
 include $(CLEAR_VARS)
 LOCAL_PACKAGE_NAME := android.core.tests.libcore.package.sun
 LOCAL_STATIC_JAVA_LIBRARIES := core-tests
diff --git a/tests/core/libcore/tests/Android.mk b/tests/core/libcore/tests/Android.mk
index bfd235f..54ffd31 100644
--- a/tests/core/libcore/tests/Android.mk
+++ b/tests/core/libcore/tests/Android.mk
@@ -14,10 +14,6 @@
 
 LOCAL_PATH:= $(call my-dir)
 
-ifeq ($(BUILD_CTSCORE_PACKAGE),)
-    $(error BUILD_CTSCORE_PACKAGE must be defined)
-endif
-
 include $(CLEAR_VARS)
 LOCAL_PACKAGE_NAME := android.core.tests.libcore.package.tests
 LOCAL_STATIC_JAVA_LIBRARIES := core-tests
diff --git a/tests/plans/CTS-flaky.xml b/tests/plans/CTS-flaky.xml
index ee64ba0..30304b4 100644
--- a/tests/plans/CTS-flaky.xml
+++ b/tests/plans/CTS-flaky.xml
@@ -8,5 +8,4 @@
   <Entry uri="android.provider" exclude="android.provider.cts.ContactsContract_CommonDataKinds_EventTest#testGetTypeLabel;android.provider.cts.MediaStore_Audio_Genres_MembersTest#testStoreAudioGenresMembersInternal;android.provider.cts.ContactsContract_DataTest#testDataInsert_updatesContactLastUpdatedTimestamp;android.provider.cts.ContactsContract_CommonDataKinds_EmailTest#testGetTypeLabel;android.provider.cts.Settings_NameValueTableTest#testPutString;android.provider.cts.CalendarTest#testCalendarEntityQuery;android.provider.cts.ContactsContract_DataUsageTest#testSingleDataUsageFeedback_incrementsCorrectDataItems;android.provider.cts.BrowserTest#testSendString;android.provider.cts.MediaStore_Video_MediaTest#testStoreVideoMediaInternal;android.provider.cts.UserDictionary_WordsTest#testAddWord_deprecated;android.provider.cts.MediaStoreIntentsTest#testAndroidTestCaseSetupProperly;android.provider.cts.ContactsContract_DeletedContacts#testQuerySinceTimestamp;android.provider.cts.BrowserTest#testRequestAllIcons;android.provider.cts.ContactsContract_CommonDataKinds_EmailTest#testAndroidTestCaseSetupProperly;android.provider.cts.CalendarTest#testReminders;android.provider.cts.MediaStore_Audio_ArtistsTest#testStoreAudioArtistsExternal;android.provider.cts.ContactsContractIntentsTest#testViewContactDir;android.provider.cts.CalendarTest#testBulkUpdate;android.provider.cts.ContactsContract_PhotoTest#testAddPhoto;android.provider.cts.Settings_NameValueTableTest#testAndroidTestCaseSetupProperly;android.provider.cts.ContactsProvider2_AccountRemovalTest#testAccountRemovalWithMergedContact_hasDeleteLogsForContacts;android.provider.cts.ContactsContract_PhotoTest#testAddEmptyPhoto;android.provider.cts.Settings_SecureTest#testGetPutFloat;android.provider.cts.MediaStore_Images_MediaTest#testGetContentUri;android.provider.cts.ContactsContract_DeletedContacts#testDelete_isUnsupported;android.provider.cts.CalendarTest#testCalendarCreationAndDeletion;android.provider.cts.BrowserTest#testBookmarksTable;android.provider.cts.Settings_SecureTest#testGetUriFor;android.provider.cts.ContactsContract_StatusUpdatesTest#testGetPresenceIconresourceId;android.provider.cts.MediaStore_Audio_Playlists_MembersTest#testStoreAudioPlaylistsMembersInternal;android.provider.cts.ContactsContract_CommonDataKinds_EventTest#testAndroidTestCaseSetupProperly;android.provider.cts.Contacts_PhonesTest#testGetDisplayLabel;android.provider.cts.ContactsContract_DeletedContacts#testInsert_isUnsupported;android.provider.cts.MediaStore_Audio_AlbumsTest#testStoreAudioAlbumsExternal;android.provider.cts.ContactsContract_DeletedContacts#testAndroidTestCaseSetupProperly;android.provider.cts.BrowserTest#testGetAllVisitedUrls;android.provider.cts.Contacts_ContactMethodsTest#testEncodeAndDecodeProtocol;android.provider.cts.ContactsContract_DeletedContacts#testQueryAll;android.provider.cts.MediaStore_Audio_MediaTest#testStoreAudioMediaInternal;android.provider.cts.ContactsContract_RawContactsTest#testRawContactPsuedoDelete_hasDeleteLogForContact;android.provider.cts.MediaStore_FilesTest#testAndroidTestCaseSetupProperly;android.provider.cts.MediaStore_Images_MediaTest#testStoreImagesMediaInternal;android.provider.cts.MediaStore_Audio_AlbumsTest#testAlbumArt;android.provider.cts.ContactsContract_ContactsTest#testLookupUri;android.provider.cts.MediaStoreIntentsTest#testPickVideoDir;android.provider.cts.MediaStore_Images_MediaTest#testInsertImageWithBitmap;android.provider.cts.MediaStoreIntentsTest#testViewAudioFile;android.provider.cts.MediaStore_Audio_Genres_MembersTest#testStoreAudioGenresMembersExternal;android.provider.cts.ContactsContract_CommonDataKinds_StructuredPostalTest#testAndroidTestCaseSetupProperly;android.provider.cts.CalendarTest#testInstanceSearch;android.provider.cts.VoicemailContractTest#testStatusTablePermissions;android.provider.cts.ContactsContract_CommonDataKinds_OrganizationTest#testAndroidTestCaseSetupProperly;android.provider.cts.SettingsTest#testAndroidTestCaseSetupProperly;android.provider.cts.MediaStore_Audio_PlaylistsTest#testStoreAudioPlaylistsInternal;android.provider.cts.ContactsContract_DumpFileProviderTest#testQuery_worksWithValidFileName;android.provider.cts.Settings_SecureTest#testAndroidTestCaseSetupProperly;android.provider.cts.MediaStore_Video_MediaTest#testAndroidTestCaseSetupProperly;android.provider.cts.BrowserTest#testAccessHistory;android.provider.cts.ContactsContract_DataTest#testContactablesFilterByPhonePrefix_returnsCorrectDataRows;android.provider.cts.MediaStore_Images_ThumbnailsTest#testStoreImagesMediaInternal;android.provider.cts.ContactsTest#testCallsTable;android.provider.cts.CalendarTest#testEventCreationAndDeletion;android.provider.cts.VoicemailContractTest#testVoicemailTablePermissions;android.provider.cts.CalendarTest#testEventsUid2445;android.provider.cts.MediaStore_VideoTest#testQuery;android.provider.cts.CalendarTest#testDefaultProjections;android.provider.cts.MediaStoreIntentsTest#testViewVideoDir;android.provider.cts.MediaStore_FilesTest#testGetContentUri;android.provider.cts.ContactsContract_ContactsTest#testInsert_isUnsupported;android.provider.cts.ContactsContract_CommonDataKinds_PhoneTest#testGetTypeLabel;android.provider.cts.VoicemailContractTest#testVoicemailsTable;android.provider.cts.ContactsContract_CommonDataKinds_RelationTest#testGetTypeLabel;android.provider.cts.Settings_SystemTest#testGetDefaultValues;android.provider.cts.ContactsContract_RawContactsTest#testAndroidTestCaseSetupProperly;android.provider.cts.MediaStoreTest#testGetMediaScannerUri;android.provider.cts.ContactsContract_RawContactsTest#testRawContactDelete_removesRecord;android.provider.cts.Settings_SecureTest#testGetPutString;android.provider.cts.ContactsTest#testGroupMembershipTable;android.provider.cts.MediaStore_Audio_MediaTest#testGetContentUri;android.provider.cts.CalendarTest#testFullRecurrenceUpdate;android.provider.cts.MediaStore_FilesTest#testCaseSensitivity;android.provider.cts.MediaStore_Audio_AlbumsTest#testAndroidTestCaseSetupProperly;android.provider.cts.MediaStore_Audio_ArtistsTest#testStoreAudioArtistsInternal;android.provider.cts.ContactsContract_ContactsTest#testContactDelete_hasDeleteLog;android.provider.cts.ContactsContract_DataUsageTest#testMultiIdDataUsageFeedback_incrementsCorrectDataItems;android.provider.cts.CalendarTest#testSyncOnlyInsertEnforcement;android.provider.cts.VoicemailContractTest#testDataColumnUpdate_throwsIllegalArgumentException;android.provider.cts.CalendarTest#testColorWriteRequirements;android.provider.cts.CalendarTest#testWhenByDayQuery;android.provider.cts.ContactsContract_StreamItemsTest#testAndroidTestCaseSetupProperly;android.provider.cts.ContactsContract_CommonDataKinds_ImTest#testAndroidTestCaseSetupProperly;android.provider.cts.MediaStore_AudioTest#testKeyFor;android.provider.cts.ContactsContract_ContactsTest#testAndroidTestCaseSetupProperly;android.provider.cts.MediaStore_Images_ThumbnailsTest#testQueryInternalThumbnails;android.provider.cts.ContactsContract_DumpFileProviderTest#testOpenFileDescriptor_throwsErrorWithIllegalFileName;android.provider.cts.CalendarTest#testEventColors;android.provider.cts.SettingsTest#testBluetoothDevicesTable;android.provider.cts.ContactsContract_RawContactsTest#testRawContactUpdate_updatesContactUpdatedTimestamp;android.provider.cts.ContactsContract_CommonDataKinds_RelationTest#testAndroidTestCaseSetupProperly;android.provider.cts.MediaStoreTest#testGetVersion;android.provider.cts.MediaStore_Audio_GenresTest#testGetContentUri;android.provider.cts.ContactsContract_DataTest#testDataDelete_updatesContactLastUpdatedTimestamp;android.provider.cts.ContactsContract_CommonDataKinds_SipAddressTest#testGetTypeLabel;android.provider.cts.BrowserTest#testSaveBookmark;android.provider.cts.ContactsProvider2_AccountRemovalTest#testAndroidTestCaseSetupProperly;android.provider.cts.MediaStore_Video_MediaTest#testGetContentUri;android.provider.cts.CalendarTest#testExtendedProperties;android.provider.cts.Settings_SystemTest#testAndroidTestCaseSetupProperly;android.provider.cts.CalendarTest#testNonAdapterRecurrenceExceptions;android.provider.cts.CalendarTest#testOutOfOrderRecurrenceExceptions;android.provider.cts.CalendarTest#testConversionToRecurring;android.provider.cts.MediaStore_Audio_Playlists_MembersTest#testStoreAudioPlaylistsMembersExternal;android.provider.cts.CalendarTest#testMultiRuleRecurrence;android.provider.cts.MediaStoreIntentsTest#testPickAudioDir;android.provider.cts.MediaStore_Audio_GenresTest#testStoreAudioGenresExternal;android.provider.cts.MediaStoreIntentsTest#testViewVideoFile;android.provider.cts.ContactsProvider2_AccountRemovalTest#testAccountRemoval_deletesContacts;android.provider.cts.Contacts_ContactMethodsTest#testAndroidTestCaseSetupProperly;android.provider.cts.ContactsContract_GroupMembershipTest#testAddGroupMembershipWithGroupRowId;android.provider.cts.TelephonyProviderTest#testOpeningAnyFile;android.provider.cts.MediaStore_Audio_GenresTest#testGetContentUriForAudioId;android.provider.cts.ContactsContract_CommonDataKinds_PhoneTest#testAndroidTestCaseSetupProperly;android.provider.cts.Contacts_PeopleTest#testMarkAsContacted;android.provider.cts.MediaStore_FilesTest#testAccess;android.provider.cts.ContactsContract_CommonDataKinds_ImTest#testGetTypeLabel;android.provider.cts.SearchRecentSuggestionsTest#testSuggestionsTable;android.provider.cts.CalendarTest#testAttendees;android.provider.cts.SettingsTest#testAccessNonTable;android.provider.cts.MediaStoreIntentsTest#testPickImageDir;android.provider.cts.BrowserTest#testSearchesTable;android.provider.cts.Contacts_SettingsTest#testAccessSetting;android.provider.cts.ContactsContract_StreamItemPhotosTest#testContentPhotoUri;android.provider.cts.ContactsContract_DataTest#testGetLookupUriByDisplayName;android.provider.cts.ContactsContract_StatusUpdatesTest#testInsertStatus;android.provider.cts.MediaStore_Video_ThumbnailsTest#testGetContentUri;android.provider.cts.MediaStore_Audio_GenresTest#testStoreAudioGenresInternal;android.provider.cts.MediaStore_Images_MediaTest#testInsertImageWithImagePath;android.provider.cts.CalendarTest#testForwardRecurrenceExceptions;android.provider.cts.Settings_SecureTest#testUnknownSourcesOffByDefault;android.provider.cts.CalendarTest#testBadRequests;android.provider.cts.ContactsTest#testSettingsTable;android.provider.cts.VoicemailContractTest#testInsert_doesNotUpdateDataColumn;android.provider.cts.ContactsContract_RawContactsTest#testRawContactCreate_updatesContactUpdatedTimestamp;android.provider.cts.MediaStoreIntentsTest#testViewImageDir;android.provider.cts.ContactsContract_ContactsTest#testContactDelete_removesContactRecord;android.provider.cts.MediaStore_Images_ThumbnailsTest#testQueryExternalMiniThumbnails;android.provider.cts.ContactsContract_ContactsTest#testContactUpdate_updatesContactUpdatedTimestamp;android.provider.cts.Settings_SettingNotFoundExceptionTest#testAndroidTestCaseSetupProperly;android.provider.cts.ContactsContract_RawContactsTest#testGetLookupUriByDisplayName;android.provider.cts.BrowserTest#testAccessSearches;android.provider.cts.ContactsContract_CommonDataKinds_SipAddressTest#testAndroidTestCaseSetupProperly;android.provider.cts.ContactsContract_GroupMembershipTest#testAddGroupMembershipWithUnknownGroupSourceId;android.provider.cts.MediaStore_Audio_MediaTest#testStoreAudioMediaExternal;android.provider.cts.MediaStore_Audio_PlaylistsTest#testGetContentUri;android.provider.cts.ContactsTest#testGroupsTable;android.provider.cts.MediaStore_Audio_AlbumsTest#testGetContentUri;android.provider.cts.Settings_SystemTest#testGetUriFor;android.provider.cts.ContactsContract_StreamItemPhotosTest#testContentDirectoryUri;android.provider.cts.SearchRecentSuggestionsTest#testSearchRecentSuggestions;android.provider.cts.Contacts_ContactMethodsTest#testAddPostalLocation;android.provider.cts.MediaStore_Audio_Artists_AlbumsTest#testGetContentUri;android.provider.cts.SearchRecentSuggestionsTest#testConstructor;android.provider.cts.ContactsContract_ContactsTest#testMarkAsContacted;android.provider.cts.ContactsTest#testPeopleTable;android.provider.cts.CalendarTest#testCalendarColors;android.provider.cts.CalendarTest#testCalendarIsPrimary;android.provider.cts.ContactsContract_RawContactsTest#testRawContactDelete_hasDeleteLogForContact;android.provider.cts.Settings_SystemTest#testSystemSettings;android.provider.cts.CalendarTest#testRecurrence;android.provider.cts.ContactsContract_GroupMembershipTest#testAddGroupMembershipWithGroupSourceId;android.provider.cts.MediaStore_Audio_Genres_MembersTest#testGetContentUri;android.provider.cts.VoicemailContractTest#testStatusTable;android.provider.cts.ContactsContract_DataTest#testContactablesFilterByFirstName_returnsCorrectDataRows;android.provider.cts.Settings_NameValueTableTest#testGetUriFor;android.provider.cts.ContactsContract_DumpFileProviderTest#testQuery_throwsErrorWithIllegalFileName;android.provider.cts.ContactsTest#testContactMethodsTable;android.provider.cts.ContactsContractIntentsTest#testPickContactDir;android.provider.cts.MediaStore_Audio_AlbumsTest#testStoreAudioAlbumsInternal;android.provider.cts.Contacts_PeopleTest#testAddToGroup;android.provider.cts.ContactsContract_DataTest#testContactablesFilterByEmailPrefix_returnsCorrectDataRows;android.provider.cts.ContactsContract_StatusUpdatesTest#testGetPresencePrecedence;android.provider.cts.BrowserTest#testUpdateVisitedHistory;android.provider.cts.SettingsTest#testSecureTable;android.provider.cts.ContactsContract_StreamItemsTest#testContentUri;android.provider.cts.MediaStore_Audio_Artists_AlbumsTest#testStoreAudioArtistsAlbumsInternal;android.provider.cts.MediaStore_Video_ThumbnailsTest#testGetThumbnail;android.provider.cts.ContactsContractIntentsTest#testGetContentContactDir;android.provider.cts.SettingsTest#testSystemTable;android.provider.cts.CalendarTest#testEventUpdateAsApp;android.provider.cts.MediaStore_Images_MediaTest#testStoreImagesMediaExternal;android.provider.cts.CalendarTest#testEventsEntityQuery;android.provider.cts.ContactsContractIntentsTest#testAndroidTestCaseSetupProperly;android.provider.cts.CalendarTest#testSyncState;android.provider.cts.MediaStore_Video_ThumbnailsTest#testAndroidTestCaseSetupProperly;android.provider.cts.ContactsContract_DataTest#testGetLookupUriBySourceId;android.provider.cts.UserDictionary_WordsTest#testAndroidTestCaseSetupProperly;android.provider.cts.ContactsTest#testExtensionsTable;android.provider.cts.ContactsContract_DataTest#testContactablesUri;android.provider.cts.ContactsContract_CommonDataKinds_ImTest#testGetProtocolLabel;android.provider.cts.Settings_SettingNotFoundExceptionTest#testConstructor;android.provider.cts.CalendarTest#testEventsIsOrganizer;android.provider.cts.ContactsContract_StreamItemsTest#testContentDirectoryUri;android.provider.cts.Contacts_PeopleTest#testAccessPhotoData;android.provider.cts.UserDictionary_WordsTest#testAddWord;android.provider.cts.CalendarTest#testSingleRecurrenceExceptions;android.provider.cts.BrowserTest#testGetAllBookmarks;android.provider.cts.ContactsContract_RawContactsTest#testRawContactDelete_setsDeleteFlag;android.provider.cts.MediaStoreIntentsTest#testViewImageFile;android.provider.cts.Settings_SecureTest#testGetPutLong;android.provider.cts.ContactsContract_DataTest#testContactablesFilterByLastName_returnsCorrectDataRows;android.provider.cts.SearchRecentSuggestionsTest#testAndroidTestCaseSetupProperly;android.provider.cts.Contacts_PhonesTest#testGetDisplayLabelCharSequenceArray;android.provider.cts.SettingsTest#testUserDictionarySettingsExists;android.provider.cts.Contacts_OrganizationsTest#testGetDisplayLabel;android.provider.cts.Settings_SecureTest#testGetDefaultValues;android.provider.cts.ContactsContract_ContactsTest#testContactDelete_marksRawContactsForDeletion;android.provider.cts.ContactsTest#testPhotosTable;android.provider.cts.ContactsContract_ContactsTest#testContentUri;android.provider.cts.MediaStore_Audio_Artists_AlbumsTest#testStoreAudioArtistsAlbumsExternal;android.provider.cts.Contacts_PhonesTest#testAndroidTestCaseSetupProperly;android.provider.cts.ContactsProvider2_AccountRemovalTest#testAccountRemovalWithMergedContact_doesNotDeleteContactAndTimestampUpdated;android.provider.cts.ContactsTest#testOrganizationsTable;android.provider.cts.MediaStore_Audio_Playlists_MembersTest#testGetContentUri;android.provider.cts.MediaStore_Audio_PlaylistsTest#testStoreAudioPlaylistsExternal;android.provider.cts.Contacts_OrganizationsTest#testAndroidTestCaseSetupProperly;android.provider.cts.ContactsTest#testPhonesTable;android.provider.cts.Settings_SecureTest#testGetPutInt;android.provider.cts.ContactsContract_StatusUpdatesTest#testAndroidTestCaseSetupProperly;android.provider.cts.MediaStore_Audio_MediaTest#testGetContentUriForPath;android.provider.cts.CalendarTest#testCalendarUpdateAsApp;android.provider.cts.ContactsContract_DeletedContacts#testQueryByContactId;android.provider.cts.ContactsProvider2_AccountRemovalTest#testAccountRemoval_hasDeleteLogsForContacts;android.provider.cts.ContactsContract_StreamItemPhotosTest#testAndroidTestCaseSetupProperly;android.provider.cts.MediaStore_Audio_ArtistsTest#testGetContentUri;android.provider.cts.ContactsContract_DumpFileProviderTest#testAndroidTestCaseSetupProperly;android.provider.cts.ContactsContract_DataTest#testDataUpdate_updatesContactLastUpdatedTimestamp;android.provider.cts.ContactsProvider2_AccountRemovalTest#testAccountRemovalWithMergedContact_deletesContacts;android.provider.cts.ContactsContract_RawContactsTest#testGetLookupUriBySourceId;android.provider.cts.MediaStore_Images_ThumbnailsTest#testGetContentUri;android.provider.cts.ContactsContract_DumpFileProviderTest#testOpenFileDescriptor_worksWithValidFileName;android.provider.cts.ContactsContract_CommonDataKinds_StructuredPostalTest#testGetTypeLabel;android.provider.cts.ContactsContract_DataUsageTest#testAndroidTestCaseSetupProperly;android.provider.cts.Contacts_ContactMethodsTest#test;android.provider.cts.MediaStore_Video_MediaTest#testStoreVideoMediaExternal;android.provider.cts.ContactsContract_CommonDataKinds_OrganizationTest#testGetTypeLabel;android.provider.cts.MediaStore_Images_ThumbnailsTest#testStoreImagesMediaExternal;android.provider.cts.ContactsContract_DataTest#testContactablesFilter_doesNotExist_returnsCorrectDataRows;android.provider.cts.ContactsContract_DeletedContacts#testQuery_returnsProperColumns" />
   <Entry uri="android.security" exclude="android.security.cts.KeystoreExploitTest#testAndroidTestCaseSetupProperly;android.security.cts.CharDeviceTest#testExynosKernelMemoryRead;android.security.cts.VoldExploitTest#testAndroidTestCaseSetupProperly;android.security.cts.LoadEffectLibraryTest#testLoadLibrary;android.security.cts.BrowserTest#testAndroidTestCaseSetupProperly;android.security.cts.LinuxRngTest#testDevUrandomMajorMinor;android.security.cts.ServicePermissionsTest#testDumpProtected;android.security.cts.BannedFilesTest#testNoSetuidTcpdump;android.security.cts.SqliteJournalLeakTest#testShm;android.security.cts.LinuxRngTest#testDevRandomMajorMinor;android.security.cts.ClonedSecureRandomTest#testCheckForDuplicateOutput;android.security.cts.BrowserTest#testBrowserPrivateDataAccess;android.security.cts.BrowserTest#testTabExhaustion;android.security.cts.KeystoreExploitTest#testKeystoreCrash;android.security.cts.SqliteJournalLeakTest#testJournal;android.security.cts.BannedFilesTest#testNoCmdClient;android.security.cts.BannedFilesTest#testNoSetuidIp;android.security.cts.BannedFilesTest#testNoSyncAgent;android.security.cts.BannedFilesTest#testNoInitRunIt;android.security.cts.ListeningPortsTest#testNoListeningLoopbackTcp6Ports;android.security.cts.KernelSettingsTest#testKptrRestrict;android.security.cts.AslrTest#testVaRandomize;android.security.cts.KernelSettingsTest#testMmapMinAddr;android.security.cts.CertificateTest#testBlockCertificates;android.security.cts.BrowserTest#testTabReuse;android.security.cts.KernelSettingsTest#testSELinuxEnforcing;android.security.cts.ListeningPortsTest#testNoListeningLoopbackUdp6Ports;android.security.cts.NativeCodeTest#testPerfEvent;android.security.cts.KernelSettingsTest#testSetuidDumpable;android.security.cts.PackageSignatureTest#testAndroidTestCaseSetupProperly;android.security.cts.ServicePermissionsTest#testAndroidTestCaseSetupProperly;android.security.cts.CertificateTest#testCertificates;android.security.cts.KernelSettingsTest#testNoConfigGz;android.security.cts.KernelSettingsTest#testDmesgRestrict;android.security.cts.ListeningPortsTest#testAndroidTestCaseSetupProperly;android.security.cts.SqliteJournalLeakTest#testAndroidTestCaseSetupProperly;android.security.cts.ListeningPortsTest#testNoRemotelyAccessibleListeningTcp6Ports;android.security.cts.ListeningPortsTest#testNoListeningLoopbackTcpPorts;android.security.cts.ClonedSecureRandomTest#testAndroidTestCaseSetupProperly;android.security.cts.VoldExploitTest#testTryCommandInjection;android.security.cts.VoldExploitTest#testZergRushCrash;android.security.cts.AslrTest#testOneExecutableIsPie;android.security.cts.VoldExploitTest#testTryToCrashVold;android.security.cts.ListeningPortsTest#testNoListeningLoopbackUdpPorts;android.security.cts.CertificateTest#testAndroidTestCaseSetupProperly;android.security.cts.SqliteJournalLeakTest#testWal;android.security.cts.ListeningPortsTest#testNoRemotelyAccessibleListeningTcpPorts;android.security.cts.CharDeviceTest#testExynosRootingVuln" />
   <Entry uri="android.webkit" exclude="android.webkit.cts.WebViewTest#testDocumentHasImages;android.webkit.cts.WebViewTest#testScrollBarOverlay;android.webkit.cts.WebViewTest#testGoBackAndForward;android.webkit.cts.WebViewTest#testGetContentHeight;android.webkit.cts.WebChromeClientTest#testOnJsConfirm;android.webkit.cts.WebChromeClientTest#testOnProgressChanged;android.webkit.cts.WebViewTest#testAddJavascriptInterfaceNullObject;android.webkit.cts.WebViewTest#testConstructor;android.webkit.cts.WebViewTest#testInvokeZoomPicker;android.webkit.cts.WebSettingsTest#testAccessMinimumLogicalFontSize;android.webkit.cts.WebViewStartupTest#testCookieManagerBlockingUiThread;android.webkit.cts.WebViewTest#testInternals;android.webkit.cts.WebViewClientTest#testShouldOverrideUrlLoading;android.webkit.cts.GeolocationTest#testSimpleGeolocationRequestAcceptOnce;android.webkit.cts.WebSettingsTest#testDatabaseDisabled;android.webkit.cts.WebView_WebViewTransportTest#testAccessWebView;android.webkit.cts.WebSettingsTest#testAccessJavaScriptCanOpenWindowsAutomatically;android.webkit.cts.WebSettingsTest#testAppCacheEnabled;android.webkit.cts.WebSettingsTest#testAccessUserAgentString;android.webkit.cts.WebViewTest#testClearHistory;android.webkit.cts.WebSettingsTest#testAccessSerifFontFamily;android.webkit.cts.WebSettingsTest#testAccessLayoutAlgorithm;android.webkit.cts.WebSettingsTest#testAccessFantasyFontFamily;android.webkit.cts.WebViewTest#testAddJavascriptInterface;android.webkit.cts.WebSettingsTest#testAccessCacheMode;android.webkit.cts.WebViewTest#testDebugDump;android.webkit.cts.WebViewTest#testSslErrorProceedResponseNotReusedForDifferentHost;android.webkit.cts.WebSettingsTest#testLocalImageLoads;android.webkit.cts.WebViewTest#testSslErrorProceedResponseReusedForSameHost;android.webkit.cts.HttpAuthHandlerTest#testUseHttpAuthUsernamePassword;android.webkit.cts.WebViewTest#testSetLayoutParams;android.webkit.cts.WebViewTest#testAppInjectedXRequestedWithHeaderIsNotOverwritten;android.webkit.cts.WebSettingsTest#testAccessUseDoubleTree;android.webkit.cts.WebViewTest#testOnReceivedSslErrorCancel;android.webkit.cts.URLUtilTest#testIsHttpUrl;android.webkit.cts.DateSorterTest#testConstants;android.webkit.cts.WebSettingsTest#testAccessFixedFontFamily;android.webkit.cts.WebSettingsTest#testSetRenderPriority;android.webkit.cts.WebViewTest#testRemoveJavascriptInterface;android.webkit.cts.WebViewTest#testAndroidAssetAnchor;android.webkit.cts.WebViewTest#testOnReceivedSslError;android.webkit.cts.CookieTest#testEmptyValue;android.webkit.cts.WebViewTest#testPauseResumeTimers;android.webkit.cts.URLUtilTest#testIsContentUrl;android.webkit.cts.WebChromeClientTest#testBlockWindowsAsync;android.webkit.cts.WebViewTest#testGetVisibleTitleHeight;android.webkit.cts.WebBackForwardListTest#testClone;android.webkit.cts.WebSettingsTest#testAccessDefaultTextEncodingName;android.webkit.cts.URLUtilTest#testGuessUrl;android.webkit.cts.MimeTypeMapTest#testAndroidTestCaseSetupProperly;android.webkit.cts.WebChromeClientTest#testOnReceivedIcon;android.webkit.cts.CookieTest#testAndroidTestCaseSetupProperly;android.webkit.cts.CookieManagerTest#testRemoveCookies;android.webkit.cts.WebSettingsTest#testAccessPluginsPath;android.webkit.cts.WebSettingsTest#testAccessAllowFileAccess;android.webkit.cts.WebSettingsTest#testAccessSupportMultipleWindows;android.webkit.cts.WebViewTest#testAppCanInjectHeadersViaImmutableMap;android.webkit.cts.WebViewTest#testSecureSiteSetsCertificate;android.webkit.cts.WebViewTest#testSetWebViewClient;android.webkit.cts.WebViewTest#testSetScrollBarStyle;android.webkit.cts.CookieTest#testDomain;android.webkit.cts.WebViewTest#testZoom;android.webkit.cts.URLUtilTest#testIsDataUrl;android.webkit.cts.CookieManagerTest#testAcceptCookie;android.webkit.cts.WebChromeClientTest#testOnReceivedTitle;android.webkit.cts.URLUtilTest#testIsFileUrl;android.webkit.cts.WebSettingsTest#testAccessJavaScriptEnabled;android.webkit.cts.URLUtilTest#testIsNetworkUrl;android.webkit.cts.WebViewTest#testFindAddress;android.webkit.cts.WebViewTest#testSetNetworkAvailable;android.webkit.cts.WebViewTest#testClearSslPreferences;android.webkit.cts.URLUtilTest#testIsHttpsUrl;android.webkit.cts.MimeTypeMapTest#testGetFileExtensionFromUrl;android.webkit.cts.WebViewTest#testGetOriginalUrl;android.webkit.cts.WebChromeClientTest#testBlockWindowsSync;android.webkit.cts.WebViewTest#testLoadData;android.webkit.cts.WebViewTest#testInsecureSiteClearsCertificate;android.webkit.cts.WebBackForwardListTest#testGetCurrentItem;android.webkit.cts.URLUtilTest#testStripAnchor;android.webkit.cts.URLUtilTest#testGuessFileName;android.webkit.cts.URLUtilTest#testAndroidTestCaseSetupProperly;android.webkit.cts.WebViewTest#testEvaluateJavascript;android.webkit.cts.DateSorterTest#testConstructor;android.webkit.cts.WebViewTest#testPageScroll;android.webkit.cts.WebSettingsTest#testIframesWhenAccessFromFileURLsEnabled;android.webkit.cts.WebViewTest#testFlingScroll;android.webkit.cts.WebSettingsTest#testXHRWhenAccessFromFileURLsEnabled;android.webkit.cts.WebChromeClientTest#testOnJsPrompt;android.webkit.cts.WebSettingsTest#testAccessSupportZoom;android.webkit.cts.WebSettingsTest#testLoadsImagesAutomatically;android.webkit.cts.URLUtilTest#testIsValidUrl;android.webkit.cts.WebViewTest#testRequestFocusNodeHref;android.webkit.cts.WebViewTest#testLoadDataWithBaseUrl;android.webkit.cts.WebChromeClientTest#testOnJsAlert;android.webkit.cts.WebSettingsTest#testAccessSansSerifFontFamily;android.webkit.cts.CookieManagerTest#testCookieManager;android.webkit.cts.WebViewTest#testSetMapTrackballToArrowKeys;android.webkit.cts.WebViewTest#testCreatingWebViewCreatesCookieSyncManager;android.webkit.cts.DateSorterTest#testGetIndex;android.webkit.cts.GeolocationTest#testGeolocationPermissions;android.webkit.cts.WebChromeClientTest#testOnJsBeforeUnload;android.webkit.cts.CookieManagerTest#testClone;android.webkit.cts.CookieManagerTest#testGetInstance;android.webkit.cts.WebViewTest#testGetZoomControls;android.webkit.cts.CookieTest#testSubDomain;android.webkit.cts.WebSettingsTest#testUserAgentString_default;android.webkit.cts.MimeTypeMapTest#testGetMimeTypeFromExtension;android.webkit.cts.WebSettingsTest#testBlockNetworkImage;android.webkit.cts.WebViewTest#testPlatformNotifications;android.webkit.cts.URLUtilTest#testIsAboutUrl;android.webkit.cts.WebViewTest#testSetPictureListener;android.webkit.cts.MimeTypeMapTest#testHasMimeType;android.webkit.cts.WebViewTest#testOnReceivedSslErrorProceed;android.webkit.cts.DateSorterTest#testGetLabel;android.webkit.cts.GeolocationTest#testSimpleGeolocationRequestAcceptAlways;android.webkit.cts.URLUtilTest#testDecode;android.webkit.cts.HttpAuthHandlerTest#testProceed;android.webkit.cts.WebSettingsTest#testSetNeedInitialFocus;android.webkit.cts.WebSettingsTest#testIframesWhenAccessFromFileURLsDisabled;android.webkit.cts.WebSettingsTest#testAccessCursiveFontFamily;android.webkit.cts.WebViewTest#testFindAll;android.webkit.cts.WebViewTest#testStopLoading;android.webkit.cts.DateSorterTest#testAndroidTestCaseSetupProperly;android.webkit.cts.WebSettingsTest#testAccessDefaultFixedFontSize;android.webkit.cts.CookieManagerTest#testb3167208;android.webkit.cts.WebSettingsTest#testAccessMinimumFontSize;android.webkit.cts.WebSettingsTest#testAccessUseWideViewPort;android.webkit.cts.WebSettingsTest#testAccessSaveFormData;android.webkit.cts.WebViewTest#testRequestChildRectangleOnScreen;android.webkit.cts.URLUtilTest#testIsJavaScriptUrl;android.webkit.cts.WebViewTest#testFindNext;android.webkit.cts.MimeTypeMapTest#testHasExtension;android.webkit.cts.WebViewTest#testSetDownloadListener;android.webkit.cts.WebSettingsTest#testXHRWhenAccessFromFileURLsDisabled;android.webkit.cts.WebViewTest#testDestroy;android.webkit.cts.MimeTypeMapTest#testGetSingleton;android.webkit.cts.WebViewTest#testAndroidAssetQueryParam;android.webkit.cts.WebViewTest#testAccessPluginList;android.webkit.cts.CookieTest#testPath;android.webkit.cts.WebViewTest#testAccessHttpAuthUsernamePassword;android.webkit.cts.WebViewTest#testUseRemovedJavascriptInterface;android.webkit.cts.WebSettingsTest#testAccessTextSize;android.webkit.cts.URLUtilTest#testIsAssetUrl;android.webkit.cts.CookieTest#testInvalidDomain;android.webkit.cts.CookieSyncManagerTest#testCookieSyncManager;android.webkit.cts.URLUtilTest#testComposeSearchUrl;android.webkit.cts.WebChromeClientTest#testWindows;android.webkit.cts.WebViewTest#testRequestImageRef;android.webkit.cts.WebSettingsTest#testAccessDefaultFontSize;android.webkit.cts.WebViewClientTest#testShouldOverrideKeyEvent;android.webkit.cts.WebHistoryItemTest#testWebHistoryItem;android.webkit.cts.WebSettingsTest#testAccessBuiltInZoomControls;android.webkit.cts.WebSettingsTest#testAppCacheDisabled;android.webkit.cts.WebViewTest#testSetWebChromeClient;android.webkit.cts.WebViewTest#testGetHitTestResult;android.webkit.cts.WebSettingsTest#testAccessStandardFontFamily;android.webkit.cts.GeolocationTest#testSimpleGeolocationRequestReject;android.webkit.cts.WebSettingsTest#testBlockNetworkLoads;android.webkit.cts.DateSorterTest#testGetBoundary;android.webkit.cts.WebViewTest#testCapturePicture;android.webkit.cts.WebSettingsTest#testAccessPluginsEnabled;android.webkit.cts.WebViewTest#testSaveAndRestoreState;android.webkit.cts.WebViewTest#testLoadUrl;android.webkit.cts.HttpAuthHandlerTest#testCancel;android.webkit.cts.URLUtilTest#testIsCookielessProxyUrl;android.webkit.cts.WebViewTest#testGetFavicon;android.webkit.cts.MimeTypeMapTest#testGetExtensionFromMimeType" />
-  <Entry uri="android.widget" exclude="android.widget.cts.ImageSwitcherTest#testSetImageDrawable;android.widget.cts.ExpandableListViewTest#testOnSaveInstanceState;android.widget.cts.RemoteViewsTest#testSetFloat;android.widget.cts.ToggleButtonTest#testConstructor;android.widget.cts.CursorTreeAdapterTest#testOnGroupCollapsed;android.widget.cts.ListViewTest#testDispatchDraw;android.widget.cts.HorizontalScrollViewTest#testRequestLayout;android.widget.cts.TextViewTest#testDrawableResolution;android.widget.cts.CursorTreeAdapterTest#testGetGroup;android.widget.cts.RemoteViewsTest#testSetBoolean;android.widget.cts.SimpleExpandableListAdapterTest#testGetChildrenCount;android.widget.cts.TimePickerTest#testSetEnabled;android.widget.cts.RemoteViewsTest#testSetChronometer;android.widget.cts.BaseExpandableListAdapterTest#testNotifyDataSetChanged;android.widget.cts.TextViewTest#testConstructor;android.widget.cts.FrameLayoutTest#testGenerateLayoutParams2;android.widget.cts.FrameLayoutTest#testGenerateLayoutParams1;android.widget.cts.DialerFilterTest#testClearText;android.widget.cts.ListViewTest#testFindViewWithTagTraversal;android.widget.cts.TextViewTest#testGetResolvedTextAlignmentWithInheritance;android.widget.cts.SimpleAdapterTest#testSetDropDownViewResource;android.widget.cts.GalleryTest#testShowContextMenu;android.widget.cts.ExpandableListViewTest#testSetChildIndicator;android.widget.cts.GalleryTest#testConstructor;android.widget.cts.CursorTreeAdapterTest#testGetGroupCount;android.widget.cts.TextViewTest#testDebug;android.widget.cts.ExpandableListViewTest#testSetIndicatorBounds;android.widget.cts.AutoCompleteTextViewTest#testOnKeyPreIme;android.widget.cts.TextViewTest#testOnTrackballEvent;android.widget.cts.ExpandableListViewTest#testAndroidTestCaseSetupProperly;android.widget.cts.PopupWindowTest#testAccessBackground;android.widget.cts.PopupWindowTest#testGetMaxAvailableHeight;android.widget.cts.TextViewTest#testGetTotalPaddingLeft;android.widget.cts.AutoCompleteTextViewTest#testAccessAdapter;android.widget.cts.AutoCompleteTextViewTest#testPerformCompletion;android.widget.cts.ZoomButtonTest#testDispatchUnhandledMove;android.widget.cts.AbsSpinnerTest#testOnMeasure;android.widget.cts.TextViewTest#testOnWindowFocusChanged;android.widget.cts.PopupWindowTest#testUpdateDimensionAndAlignAnchorViewWithOffsets;android.widget.cts.GridLayoutTest#testCheckLayoutParams;android.widget.cts.TabHostTest#testNewTabSpec;android.widget.cts.ViewAnimatorTest#testGetCurrentView;android.widget.cts.ListViewTest#testDispatchKeyEvent;android.widget.cts.TextViewTest#testSetMinEms;android.widget.cts.FrameLayoutTest#testGatherTransparentRegion;android.widget.cts.AbsListViewTest#testDraw;android.widget.cts.ZoomControlsTest#testHasFocus;android.widget.cts.RemoteViews_ActionExceptionTest#testConstructor;android.widget.cts.ViewFlipperTest#testActivityTestCaseSetUpProperly;android.widget.cts.RemoteViewsTest#testSetString;android.widget.cts.SimpleExpandableListAdapterTest#testConstructor;android.widget.cts.GalleryTest#testComputeHorizontalScrollExtent;android.widget.cts.DialerFilterTest#testGetFilterText;android.widget.cts.RadioGroupTest#testGenerateDefaultLayoutParams;android.widget.cts.DialerFilterTest#testSetFilterWatcher;android.widget.cts.CheckBoxTest#testConstructor;android.widget.cts.ProgressBarTest#testDrawableStateChanged;android.widget.cts.MultiAutoCompleteTextViewTest#testPerformFiltering;android.widget.cts.TextViewTest#testOnKeyMultiple;android.widget.cts.ProgressBarTest#testPostInvalidate;android.widget.cts.SlidingDrawerTest#testOpenAndClose;android.widget.cts.AutoCompleteTextViewTest#testConstructor;android.widget.cts.TextViewTest#testSetLineSpacing;android.widget.cts.ListViewTest#testFindViewTraversal;android.widget.cts.RadioGroupTest#testGetCheckedRadioButtonId;android.widget.cts.TabHostTest#testSetup2;android.widget.cts.TableLayout_LayoutParamsTest#testConstructor;android.widget.cts.HorizontalScrollViewTest#testRequestChildFocus;android.widget.cts.TabWidgetTest#testDispatchDraw;android.widget.cts.PopupWindowTest#testUpdate;android.widget.cts.BaseAdapterTest#testNotifyDataSetInvalidated;android.widget.cts.ProgressBarTest#testOnSaveAndRestoreInstanceState;android.widget.cts.TabHostTest#testSetup1;android.widget.cts.TextViewTest#testOnMeasure;android.widget.cts.CompoundButtonTest#testAccessInstanceState;android.widget.cts.TabWidgetTest#testOnFocusChange;android.widget.cts.DialerFilterTest#testOnFinishInflate;android.widget.cts.ImageViewTest#testSetSelected;android.widget.cts.TextViewTest#testDrawableResolution2;android.widget.cts.ExpandableListViewWithHeadersTest#testPreconditions;android.widget.cts.AbsListViewTest#testSetFilterText;android.widget.cts.ExpandableListViewTest#testGetAdapter;android.widget.cts.TextViewTest#testSingleLine;android.widget.cts.HorizontalScrollViewTest#testComputeScroll;android.widget.cts.CursorAdapterTest#testRunQueryOnBackgroundThread;android.widget.cts.ToastTest#testMakeText2;android.widget.cts.CursorAdapterTest#testGetView;android.widget.cts.ViewSwitcherTest#testSetFactory;android.widget.cts.ToastTest#testMakeText1;android.widget.cts.GridViewTest#testAccessStretchMode;android.widget.cts.AutoCompleteTextViewTest#testGetThreshold;android.widget.cts.RemoteViewsTest#testConstructor;android.widget.cts.AbsListViewTest#testCheckLayoutParams;android.widget.cts.ViewAnimatorTest#testSetAnimateFirstView;android.widget.cts.DigitalClockTest#testActivityTestCaseSetUpProperly;android.widget.cts.SlidingDrawerTest#testAnimateOpenAndClose;android.widget.cts.PopupWindowTest#testAccessFocusable;android.widget.cts.TimePickerTest#testSetOnTimeChangedListener;android.widget.cts.ScrollViewTest#testGetMaxScrollAmount;android.widget.cts.CursorAdapterTest#testOnContentChanged;android.widget.cts.TextViewTest#testGetTotalPaddingBottom;android.widget.cts.AnalogClockTest#testOnMeasure;android.widget.cts.RadioGroupTest#testCheck;android.widget.cts.CursorTreeAdapterTest#testNotifyDataSetChanged;android.widget.cts.TwoLineListItemTest#testActivityTestCaseSetUpProperly;android.widget.cts.AbsListViewTest#testGetContextMenuInfo;android.widget.cts.ViewAnimatorTest#testAccessOutAnimation;android.widget.cts.SlidingDrawerTest#testConstructor;android.widget.cts.TimePickerTest#testOnSaveInstanceStateAndOnRestoreInstanceState;android.widget.cts.AbsListViewTest#testAccessFastScrollEnabled;android.widget.cts.BaseAdapterTest#testGetItemViewType;android.widget.cts.AbsListViewTest#testSetOnScrollListener;android.widget.cts.ImageViewTest#testSetImageURI;android.widget.cts.RadioGroupTest#testOnFinishInflate;android.widget.cts.TableRowTest#testGenerateLayoutParams;android.widget.cts.DialerFilterTest#testGetLetters;android.widget.cts.HorizontalScrollViewTest#testComputeHorizontalScrollRange;android.widget.cts.TextViewTest#testSetPadding;android.widget.cts.VideoViewTest#testPlayVideo1;android.widget.cts.ArrayAdapterTest#testRemove;android.widget.cts.GridViewTest#testGetNumColumns;android.widget.cts.AbsSpinnerTest#testSetSelectionIntBoolean;android.widget.cts.LayoutDirectionTest#testDirectionForAllLayoutsWithCode;android.widget.cts.SimpleCursorAdapterTest#testAccessStringConversionColumn;android.widget.cts.ViewAnimatorTest#testGetBaseline;android.widget.cts.ChronometerTest#testConstructor;android.widget.cts.ResourceCursorTreeAdapterTest#testConstructor;android.widget.cts.AdapterViewTest#testGetPositionForView;android.widget.cts.GridViewTest#testSetVerticalSpacing;android.widget.cts.ButtonTest#testAndroidTestCaseSetupProperly;android.widget.cts.ToggleButtonTest#testToggleText;android.widget.cts.RatingBarTest#testSetMax;android.widget.cts.RemoteViewsTest#testSetViewVisibility;android.widget.cts.ScrollViewTest#testOnTouchEvent;android.widget.cts.BaseAdapterTest#testIsEnabled;android.widget.cts.ExpandableListViewTest#testSetOnChildClickListener;android.widget.cts.EditTextTest#testAndroidTestCaseSetupProperly;android.widget.cts.TextViewTest#testMarquee;android.widget.cts.ImageViewTest#testActivityTestCaseSetUpProperly;android.widget.cts.RadioGroupTest#testConstructors;android.widget.cts.DialerFilterTest#testAccessMode;android.widget.cts.DatePickerTest#testInit;android.widget.cts.TextViewTest#testGetTextColors;android.widget.cts.ProgressBarTest#testAccessProgress;android.widget.cts.TextViewTest#testGetPaint;android.widget.cts.SimpleExpandableListAdapterTest#testNewGroupView;android.widget.cts.AdapterView_AdapterContextMenuInfoTest#testConstructor;android.widget.cts.CompoundButtonTest#testConstructor;android.widget.cts.ImageViewTest#testSetImageLevel;android.widget.cts.SimpleCursorTreeAdapterTest#testBindGroupView;android.widget.cts.SimpleExpandableListAdapterTest#testGetGroup;android.widget.cts.TabWidgetTest#testFocusCurrentTab;android.widget.cts.RelativeLayoutTest#testOnLayout;android.widget.cts.ScrollViewTest#testComputeScroll;android.widget.cts.TextViewTest#testAccessHintTextColor;android.widget.cts.TableLayoutTest#testAccessShrinkAllColumns;android.widget.cts.RemoteViewsTest#testGetPackage;android.widget.cts.GridViewTest#testAttachLayoutAnimationParameters;android.widget.cts.LinearLayout_LayoutParamsTest#testAndroidTestCaseSetupProperly;android.widget.cts.CompoundButtonTest#testAndroidTestCaseSetupProperly;android.widget.cts.ImageViewTest#testSetImageDrawable;android.widget.cts.AdapterViewTest#testAccessVisiblePosition;android.widget.cts.ListViewTest#testSaveAndRestoreInstanceState;android.widget.cts.CompoundButtonTest#testToggle;android.widget.cts.TextViewTest#testMoveCursorToVisibleOffset;android.widget.cts.AutoCompleteTextViewTest#testAccessDropDownWidth;android.widget.cts.RadioGroupTest#testGenerateLayoutParams;android.widget.cts.AdapterViewTest#testCanAnimate;android.widget.cts.ScrollViewTest#testOnLayout;android.widget.cts.AutoCompleteTextViewTest#testEnoughToFilter;android.widget.cts.CheckedTextViewTest#testOnCreateDrawableState;android.widget.cts.RelativeLayout_LayoutParamsTest#testDebug;android.widget.cts.ToastTest#testAccessDuration;android.widget.cts.ToggleButtonTest#testSetChecked;android.widget.cts.HeaderViewListAdapterTest#testGetFootersCount;android.widget.cts.GridLayoutTest#testGenerateDefaultLayoutParams;android.widget.cts.AbsListViewTest#testAccessSmoothScrollbarEnabled;android.widget.cts.SlidingDrawerTest#testSetOnDrawerOpenListener;android.widget.cts.HeaderViewListAdapterTest#testGetHeadersCount;android.widget.cts.RemoteViewsTest#testSetLong;android.widget.cts.SlidingDrawerTest#testSetOnDrawerCloseListener;android.widget.cts.TextViewTest#testGetResolvedTextDirectionLtrWithInheritance;android.widget.cts.ScrollViewTest#testFullScroll;android.widget.cts.RelativeLayout_LayoutParamsTest#testAccessRule1;android.widget.cts.BaseExpandableListAdapterTest#testAreAllItemsEnabled;android.widget.cts.RelativeLayout_LayoutParamsTest#testAccessRule2;android.widget.cts.RemoteViewsActivityTest#testDerivedClass;android.widget.cts.ToastTest#testShowFailure;android.widget.cts.SimpleAdapterTest#testSetViewImage;android.widget.cts.TextViewTest#testAccessPrivateImeOptions;android.widget.cts.AdapterView_AdapterContextMenuInfoTest#testAndroidTestCaseSetupProperly;android.widget.cts.TwoLineListItemTest#testOnFinishInflate;android.widget.cts.ExpandableListViewTest#testGetExpandableListPosition;android.widget.cts.AbsListViewTest#testAccessListPadding;android.widget.cts.ExpandableListViewTest#testExpandGroup;android.widget.cts.TextViewTest#testGetUrls;android.widget.cts.LinearLayoutTest#testAccessBaselineAlignedChildIndex;android.widget.cts.CheckBoxTest#testAndroidTestCaseSetupProperly;android.widget.cts.ZoomControlsTest#testSetOnZoomInClickListener;android.widget.cts.AbsSpinnerTest#testGetCount;android.widget.cts.SimpleExpandableListAdapterTest#testNewChildView;android.widget.cts.ViewSwitcherTest#testConstructor;android.widget.cts.AutoCompleteTextViewTest#testReplaceText;android.widget.cts.ScrollViewTest#testAccessFillViewport;android.widget.cts.ToastTest#testShow;android.widget.cts.TextViewTest#testDrawableStateChanged;android.widget.cts.TimePickerTest#testGetBaseline;android.widget.cts.TextViewTest#testOnTouchEvent;android.widget.cts.ListViewTest#testOnFocusChanged;android.widget.cts.ImageViewTest#testDrawableStateChanged;android.widget.cts.FrameLayoutTest#testOnLayout;android.widget.cts.ListViewTest#testAccessItemsCanFocus;android.widget.cts.AutoCompleteTextViewTest#testOnCommitCompletion;android.widget.cts.ScrollViewTest#testAccessSmoothScrollingEnabled;android.widget.cts.TextViewTest#testSelection;android.widget.cts.CheckedTextViewTest#testOnDraw;android.widget.cts.ViewAnimatorTest#testConstructor;android.widget.cts.RadioGroupTest#testInternalPassThroughHierarchyChangeListener;android.widget.cts.SimpleCursorAdapterTest#testSetViewImage;android.widget.cts.AdapterViewTest#testGetSelected;android.widget.cts.ExpandableListViewWithHeadersTest#testSelectedPosition;android.widget.cts.ProgressBarTest#testAccessIndeterminateDrawable;android.widget.cts.TableLayoutTest#testSetOnHierarchyChangeListener;android.widget.cts.ExpandableListViewTest#testSetOnItemClickListener;android.widget.cts.ProgressBarTest#testSetVisibility;android.widget.cts.AutoCompleteTextViewTest#testConvertSelectionToString;android.widget.cts.ImageViewTest#testSetImageResource;android.widget.cts.ScrollViewTest#testGetVerticalFadingEdgeStrengths;android.widget.cts.FilterTest#testFilter2;android.widget.cts.CursorTreeAdapterTest#testAndroidTestCaseSetupProperly;android.widget.cts.FilterTest#testFilter1;android.widget.cts.ZoomControlsTest#testSetIsZoomOutEnabled;android.widget.cts.ScrollViewTest#testMeasureChildWithMargins;android.widget.cts.CompoundButtonTest#testSetOnCheckedChangeListener;android.widget.cts.TextViewTest#testGetEditableText;android.widget.cts.HorizontalScrollViewTest#testOnRequestFocusInDescendants;android.widget.cts.AbsSpinnerTest#testAccessAdapter;android.widget.cts.ChronometerTest#testAccessOnChronometerTickListener;android.widget.cts.HeaderViewListAdapterTest#testConstructor;android.widget.cts.FrameLayoutTest#testSetForegroundGravity;android.widget.cts.AbsSpinnerTest#testGetSelectedView;android.widget.cts.TextViewTest#testSetGetTextAlignment;android.widget.cts.TextViewTest#testGetLayout;android.widget.cts.ImageViewTest#testConstructor;android.widget.cts.ImageViewTest#testInvalidateDrawable;android.widget.cts.PopupWindowTest#testShowAsDropDownWithOffsets;android.widget.cts.PopupWindowTest#testIsAboveAnchor;android.widget.cts.AutoCompleteTextViewTest#testPerformFiltering;android.widget.cts.ViewFlipperTest#testConstructor;android.widget.cts.DatePickerTest#testUpdateDate;android.widget.cts.MultiAutoCompleteTextViewTest#testMultiAutoCompleteTextView;android.widget.cts.MultiAutoCompleteTextView_CommaTokenizerTest#testConstructor;android.widget.cts.RemoteViewsTest#testSetOnClickPendingIntent;android.widget.cts.HorizontalScrollViewTest#testScrollTo;android.widget.cts.HorizontalScrollViewTest#testOnTouchEvent;android.widget.cts.ListViewTest#testOnMeasure;android.widget.cts.TextViewTest#testSetHighlightColor;android.widget.cts.BaseExpandableListAdapterTest#testNotifyDataSetInvalidated;android.widget.cts.ExpandableListViewWithHeadersTest#testConvertionBetweenFlatAndPacked;android.widget.cts.HeaderViewListAdapterTest#testRemoveFooter;android.widget.cts.ListViewTest#testTransientStateStableIds;android.widget.cts.ScrollViewTest#testSmoothScrollBy;android.widget.cts.ViewSwitcherTest#testAndroidTestCaseSetupProperly;android.widget.cts.TextViewTest#testAccessAutoLinkMask;android.widget.cts.ScrollerTest#testScrollModeWithDefaultDuration;android.widget.cts.GalleryTest#testComputeHorizontalScrollRange;android.widget.cts.CheckedTextViewTest#testSetPadding;android.widget.cts.CursorTreeAdapterTest#testGetGroupId;android.widget.cts.RemoteViewsTest#testApply;android.widget.cts.DialerFilterTest#testIsQwertyKeyboard;android.widget.cts.GalleryTest#testShowContextMenuForChild;android.widget.cts.ScrollViewTest#testPageScroll;android.widget.cts.SimpleCursorTreeAdapterTest#testSetViewImage;android.widget.cts.TableLayoutTest#testOnLayout;android.widget.cts.ArrayAdapterTest#testGetPosition;android.widget.cts.TabWidgetTest#testOnSizeChanged;android.widget.cts.SimpleCursorAdapterTest#testNewDropDownView;android.widget.cts.AutoCompleteTextViewTest#testAccessListSelection;android.widget.cts.TextViewTest#testSetText1;android.widget.cts.TextViewTest#testSetText2;android.widget.cts.RemoteViewsActivityTest#testWebView;android.widget.cts.TextViewTest#testSetText3;android.widget.cts.ToastTest#testAccessGravity;android.widget.cts.HorizontalScrollViewTest#testAddViewWithIndex;android.widget.cts.ResourceCursorTreeAdapterTest#testNewChildView;android.widget.cts.HeaderViewListAdapterTest#testAndroidTestCaseSetupProperly;android.widget.cts.ExpandableListViewTest#testGetPackedPositionForGroup;android.widget.cts.ZoomControlsTest#testShowAndHide;android.widget.cts.CompoundButtonTest#testAccessChecked;android.widget.cts.SimpleAdapterTest#testGetView;android.widget.cts.DialerFilterTest#testGetDigits;android.widget.cts.ArrayAdapterTest#testDataChangeEvent;android.widget.cts.ImageSwitcherTest#testAndroidTestCaseSetupProperly;android.widget.cts.SimpleExpandableListAdapterTest#testGetChildView;android.widget.cts.ZoomControlsTest#testConstructor;android.widget.cts.CompoundButtonTest#testVerifyDrawable;android.widget.cts.TextViewTest#testSetEms;android.widget.cts.AbsSpinnerTest#testGenerateDefaultLayoutParams;android.widget.cts.TextViewTest#testAccessError;android.widget.cts.ZoomButtonTest#testOnTouchEvent;android.widget.cts.TextViewTest#testSetMaxLinesException;android.widget.cts.CompoundButtonTest#testSetButtonDrawableById;android.widget.cts.FrameLayoutTest#testGenerateDefaultLayoutParams;android.widget.cts.HorizontalScrollViewTest#testComputeScrollDeltaToGetChildRectOnScreen;android.widget.cts.ExpandableListViewTest#testSetSelectedChild;android.widget.cts.MediaControllerTest#testShow;android.widget.cts.CursorAdapterTest#testGetDropDownView;android.widget.cts.ListViewTest#testNoSelectableItems;android.widget.cts.TextViewTest#testGetBaseLine;android.widget.cts.AbsSpinnerTest#testConstructor;android.widget.cts.ListViewTest#testAccessDividerHeight;android.widget.cts.TabWidgetTest#testChildDrawableStateChanged;android.widget.cts.AbsListViewTest#testAccessSelectedItem;android.widget.cts.ScrollerTest#testTimePassed;android.widget.cts.ArrayAdapterTest#testGetItem;android.widget.cts.ImageViewTest#testVerifyDrawable;android.widget.cts.ProgressBarTest#testConstructor;android.widget.cts.ProgressBarTest#testIncrementSecondaryProgressBy;android.widget.cts.CursorTreeAdapterTest#testSetChildrenCursor;android.widget.cts.FrameLayoutTest#testCheckLayoutParams;android.widget.cts.AbsoluteLayoutTest#testGenerateDefaultLayoutParams;android.widget.cts.ZoomButtonTest#testOnKeyUp;android.widget.cts.BaseExpandableListAdapterTest#testGetCombinedId;android.widget.cts.ListViewTest#testSetSelection;android.widget.cts.SimpleCursorAdapterTest#testNewView;android.widget.cts.AutoCompleteTextViewTest#testPopupWindow;android.widget.cts.TextView_SaveStateTest#testWriteToParcel;android.widget.cts.TabHost_TabSpecTest#testSetContent2;android.widget.cts.SeekBarTest#testConstructor;android.widget.cts.TabHost_TabSpecTest#testSetContent1;android.widget.cts.TextViewTest#testSetExtractedText;android.widget.cts.TabHost_TabSpecTest#testSetContent3;android.widget.cts.SimpleExpandableListAdapterTest#testGetGroupCount;android.widget.cts.TextViewTest#testComputeVerticalScrollRange;android.widget.cts.GridLayoutTest#testAlignment;android.widget.cts.ExpandableListViewTest#testGetPackedPositionForChild;android.widget.cts.FrameLayoutTest#testConstructor;android.widget.cts.CursorAdapterTest#testAndroidTestCaseSetupProperly;android.widget.cts.RelativeLayoutTest#testSetHorizontalGravity;android.widget.cts.GalleryTest#testDispatchKeyEvent;android.widget.cts.ToastTest#testSetText1;android.widget.cts.HorizontalScrollViewTest#testFullScroll;android.widget.cts.RemoteViewsTest#testSetCharSequence;android.widget.cts.ToastTest#testSetText2;android.widget.cts.HeaderViewListAdapterTest#testRemoveHeader;android.widget.cts.TextSwitcherTest#testSetCurrentText;android.widget.cts.TwoLineListItemTest#testConstructor;android.widget.cts.MediaControllerTest#testMediaController;android.widget.cts.ListViewTest#testConstructor;android.widget.cts.AbsListViewTest#testAccessSelector;android.widget.cts.BaseExpandableListAdapterTest#testDataSetObserver;android.widget.cts.BaseAdapterTest#testHasStableIds;android.widget.cts.TextViewTest#testAccessImeActionLabel;android.widget.cts.CompoundButtonTest#testSetButtonDrawableByDrawable;android.widget.cts.AbsListViewTest#testLayoutChildren;android.widget.cts.ImageViewTest#testClearColorFilter;android.widget.cts.TextViewTest#testGetResolvedTextDirectionRtl;android.widget.cts.CheckedTextViewTest#testChecked;android.widget.cts.MultiAutoCompleteTextViewTest#testReplaceText;android.widget.cts.DigitalClockTest#testOnDetachedFromWindow;android.widget.cts.CursorTreeAdapterTest#testGetChildView;android.widget.cts.FrameLayout_LayoutParamsTest#testAndroidTestCaseSetupProperly;android.widget.cts.ImageViewTest#testSetMaxWidth;android.widget.cts.TabHostTest#testDispatchWindowFocusChanged;android.widget.cts.DigitalClockTest#testConstructor;android.widget.cts.DialerFilterTest#testOnKeyUpDown;android.widget.cts.TextViewTest#testSetHorizontallyScrolling;android.widget.cts.HeaderViewListAdapterTest#testGetViewTypeCount;android.widget.cts.AbsSeekBarTest#testAccessKeyProgressIncrement;android.widget.cts.AbsSeekBarTest#testSetThumb;android.widget.cts.ProgressBarTest#testOnDraw;android.widget.cts.TextViewTest#testSetText;android.widget.cts.PopupWindowTest#testAccessContentView;android.widget.cts.GridLayoutTest#testConstructor;android.widget.cts.PopupWindowTest#testAccessHeight;android.widget.cts.ToggleButtonTest#testAndroidTestCaseSetupProperly;android.widget.cts.ProgressBarTest#testVerifyDrawable;android.widget.cts.TableRowTest#testOnLayout;android.widget.cts.RadioGroupTest#testInternalCheckedStateTracker;android.widget.cts.HorizontalScrollViewTest#testOnSizeChanged;android.widget.cts.ResourceCursorAdapterTest#testNewView;android.widget.cts.MultiAutoCompleteTextView_CommaTokenizerTest#testFindTokenEnd;android.widget.cts.ZoomButtonTest#testOnLongClick;android.widget.cts.AnalogClockTest#testOnDetachedFromWindow;android.widget.cts.TextViewTest#testSetMaxLines;android.widget.cts.ExpandableListViewTest#testPerformItemClick;android.widget.cts.TextViewTest#testResetTextDirection;android.widget.cts.ScrollViewTest#testScrollTo;android.widget.cts.TableLayoutTest#testGenerateDefaultLayoutParams;android.widget.cts.ImageViewTest#testSetAdjustViewBounds;android.widget.cts.TableRowTest#testCheckLayoutParams;android.widget.cts.CheckedTextViewTest#testToggle;android.widget.cts.RemoteViewsTest#testSetProgressBar;android.widget.cts.VideoViewTest#testSetOnErrorListener;android.widget.cts.GalleryTest#testDispatchSetPressed;android.widget.cts.HorizontalScrollViewTest#testSmoothScrollTo;android.widget.cts.ZoomControlsTest#testSetZoomSpeed;android.widget.cts.ExpandableListViewTest#testSetAdapter;android.widget.cts.AutoCompleteTextViewTest#testOnFilterComplete;android.widget.cts.ImageViewTest#testGetDrawable;android.widget.cts.CursorTreeAdapterTest#testRunQueryOnBackgroundThread;android.widget.cts.SlidingDrawerTest#testGetHandle;android.widget.cts.BaseAdapterTest#testAndroidTestCaseSetupProperly;android.widget.cts.MediaControllerTest#testOnTrackballEvent;android.widget.cts.GalleryTest#testGenerateLayoutParams;android.widget.cts.PopupWindowTest#testConstructor;android.widget.cts.GalleryTest#testFoo;android.widget.cts.PopupWindowTest#testAccessAnimationStyle;android.widget.cts.ToggleButtonTest#testAccessTextOn;android.widget.cts.AbsoluteLayoutTest#testGenerateLayoutParams1;android.widget.cts.TextViewTest#testGetDefaultEditable;android.widget.cts.AbsoluteLayoutTest#testGenerateLayoutParams2;android.widget.cts.ArrayAdapterTest#testGetFilter;android.widget.cts.CheckedTextViewTest#testSetCheckMarkDrawableById;android.widget.cts.ScrollerTest#testAbortAnimation;android.widget.cts.LinearLayout_LayoutParamsTest#testConstructor;android.widget.cts.TextViewTest#testComputeVerticalScrollExtent;android.widget.cts.AbsSpinnerTest#testSetSelectionInt;android.widget.cts.TabHostTest#testGetTabContentView;android.widget.cts.TextViewTest#testGetDefaultMovementMethod;android.widget.cts.ScrollViewTest#testRequestChildRectangleOnScreen;android.widget.cts.TabHostTest#testGetCurrentView;android.widget.cts.TextViewTest#testAccessFilters;android.widget.cts.GalleryTest#testCheckLayoutParams;android.widget.cts.RadioGroup_LayoutParamsTest#testConstructor;android.widget.cts.BaseExpandableListAdapterTest#testOnGroupExpanded;android.widget.cts.MultiAutoCompleteTextView_CommaTokenizerTest#testTerminateToken;android.widget.cts.ScrollViewTest#testConstructor;android.widget.cts.ListViewTest#testLayoutChildren;android.widget.cts.RemoteViewsTest#testSetImageViewUri;android.widget.cts.ViewAnimatorTest#testAccessDisplayedChild;android.widget.cts.ExpandableListViewTest#testCollapseGroup;android.widget.cts.AnalogClockTest#testConstructor;android.widget.cts.CursorAdapterTest#testConvertToString;android.widget.cts.BaseAdapterTest#testIsEmpty;android.widget.cts.ResourceCursorTreeAdapterTest#testNewGroupView;android.widget.cts.ScrollViewTest#testFling;android.widget.cts.SlidingDrawerTest#testOnMeasure;android.widget.cts.TextViewTest#testSetMaxEms;android.widget.cts.LinearLayoutTest#testGenerateLayoutParams;android.widget.cts.PopupWindowTest#testAccessInputMethodMode;android.widget.cts.ListViewTest#testPerformItemClick;android.widget.cts.TextViewTest#testOnTextChanged;android.widget.cts.GridViewTest#testSetSelection;android.widget.cts.CursorAdapterTest#testHasStableIds;android.widget.cts.ProgressBarTest#testSetIndeterminate;android.widget.cts.TableRowTest#testGetVirtualChildCount;android.widget.cts.AutoCompleteTextViewTest#testOnTextChanged;android.widget.cts.TextViewTest#testSetLinesException;android.widget.cts.TextViewTest#testLength;android.widget.cts.RelativeLayoutTest#testSetGravity;android.widget.cts.RelativeLayoutTest#testConstructor;android.widget.cts.TextViewTest#testGetLineHeight;android.widget.cts.GalleryTest#testSetAnimationDuration;android.widget.cts.PopupWindowTest#testAccessTouchable;android.widget.cts.RelativeLayoutTest#testSetVerticalGravity;android.widget.cts.CursorTreeAdapterTest#testConstructor;android.widget.cts.AbsSpinnerTest#testRequestLayout;android.widget.cts.TextViewTest#testOnKeyShortcut;android.widget.cts.TableRowTest#testGetVirtualChildAt;android.widget.cts.SpinnerTest#testConstructor;android.widget.cts.SimpleAdapterTest#testGetDropDownView;android.widget.cts.ScrollViewTest#testAddViewWithLayoutParams;android.widget.cts.HeaderViewListAdapterTest#testGetItemViewType;android.widget.cts.RadioGroupTest#testSetOnCheckedChangeListener;android.widget.cts.ToggleButtonTest#testOnFinishInflate;android.widget.cts.DialerFilterTest#testAppend;android.widget.cts.HorizontalScrollViewTest#testRequestChildRectangleOnScreen;android.widget.cts.RadioButtonTest#testConstructor;android.widget.cts.PopupWindowTest#testDismiss;android.widget.cts.TableLayoutTest#testAddView2;android.widget.cts.TableLayoutTest#testAddView3;android.widget.cts.TabHostTest#testOnTouchModeChanged;android.widget.cts.TableLayoutTest#testAddView4;android.widget.cts.HeaderViewListAdapterTest#testGetWrappedAdapter;android.widget.cts.TableLayoutTest#testAddView1;android.widget.cts.ImageSwitcherTest#testSetImageResource;android.widget.cts.ToggleButtonTest#testDrawableStateChanged;android.widget.cts.EditTextTest#testSetSelectionIndex;android.widget.cts.EditTextTest#testExtendSelection;android.widget.cts.AbsSeekBarTest#testSetMax;android.widget.cts.CursorTreeAdapterTest#testChangeCursor;android.widget.cts.AdapterViewTest#testItemOrItemIdAtPosition;android.widget.cts.HorizontalScrollViewTest#testDispatchKeyEvent;android.widget.cts.TableRowTest#testGenerateDefaultLayoutParams;android.widget.cts.AbsListViewTest#testSetRecyclerListener;android.widget.cts.GalleryTest#testGetContextMenuInfo;android.widget.cts.AbsListViewTest#testAccessStackFromBottom;android.widget.cts.TextViewTest#testAccessText;android.widget.cts.SpinnerTest#testGetBaseline;android.widget.cts.ScrollViewTest#testRequestChildFocus;android.widget.cts.LinearLayoutTest#testGenerateDefaultLayoutParams;android.widget.cts.ButtonTest#testConstructor;android.widget.cts.ResourceCursorAdapterTest#testSetViewResource;android.widget.cts.ExpandableListViewBasicTest#testContextMenus;android.widget.cts.FrameLayoutTest#testVerifyDrawable;android.widget.cts.ExpandableListViewTest#testGetPackedPositionType;android.widget.cts.LinearLayoutTest#testLayoutHorizontal;android.widget.cts.ScrollViewTest#testComputeVerticalScrollRange;android.widget.cts.GridViewTest#testOnFocusChanged;android.widget.cts.ZoomControlsTest#testOnTouchEvent;android.widget.cts.GridViewTest#testActivityTestCaseSetUpProperly;android.widget.cts.ListViewTest#testAccessItemChecked;android.widget.cts.AbsoluteLayoutTest#testConstructor;android.widget.cts.TextViewTest#testComputeHorizontalScrollRange;android.widget.cts.HorizontalScrollViewTest#testAddViewWithLayoutParams;android.widget.cts.RemoteViewsTest#testGetLayoutId;android.widget.cts.CursorTreeAdapterTest#testNotifyDataSetChangedBoolean;android.widget.cts.TableRow_LayoutParamsTest#testSetBaseAttributes;android.widget.cts.HorizontalScrollViewTest#testMeasureChildWithMargins;android.widget.cts.TextViewTest#testSetIncludeFontPadding;android.widget.cts.HorizontalScrollViewTest#testAddView;android.widget.cts.TimePickerTest#testAccessCurrentMinute;android.widget.cts.TextViewTest#testGetExtendedPaddingBottom;android.widget.cts.BaseExpandableListAdapterTest#testIsEmpty;android.widget.cts.SimpleAdapterTest#testAccessViewBinder;android.widget.cts.SpinnerTest#testOnLayout;android.widget.cts.AutoCompleteTextViewTest#testAccessValidater;android.widget.cts.ScrollViewTest#testAddViewWithIndex;android.widget.cts.FrameLayoutTest#testDrawableStateChanged;android.widget.cts.PopupWindowTest#testSetTouchInterceptor;android.widget.cts.SimpleAdapterTest#testGetItem;android.widget.cts.ImageViewTest#testSetColorFilter1;android.widget.cts.ImageViewTest#testSetColorFilter2;android.widget.cts.SlidingDrawerTest#testDispatchDraw;android.widget.cts.TextViewTest#testSetOnEditorActionListener;android.widget.cts.DatePickerTest#testSetEnabled;android.widget.cts.RemoteViewsTest#testReapply;android.widget.cts.TextViewTest#testCompound;android.widget.cts.AdapterViewTest#testAccessEmptyView;android.widget.cts.Gallery_LayoutParamsTest#testConstructor;android.widget.cts.TextViewTest#testGetResolvedTextDirectionRtlWithInheritance;android.widget.cts.PopupWindowTest#testShowAtLocation;android.widget.cts.TextViewTest#testPerformLongClick;android.widget.cts.ExpandableListViewTest#testSetChildDivider;android.widget.cts.PopupWindowTest#testAccessClippingEnabled;android.widget.cts.RemoteViewsTest#testSetTextViewText;android.widget.cts.ScrollViewTest#testMeasureChild;android.widget.cts.TableLayoutTest#testAccessColumnShrinkable;android.widget.cts.TableRow_LayoutParamsTest#testConstructor;android.widget.cts.GalleryTest#testComputeHorizontalScrollOffset;android.widget.cts.ProgressBarTest#testOnMeasure;android.widget.cts.ProgressBarTest#testAccessProgressDrawable;android.widget.cts.SimpleAdapterTest#testGetFilter;android.widget.cts.RadioGroupTest#testSetOnHierarchyChangeListener;android.widget.cts.AbsListViewTest#testHandleDataChanged;android.widget.cts.AbsListViewTest#testShowContextMenuForChild;android.widget.cts.ExpandableListViewTest#testConstructor;android.widget.cts.HeaderViewListAdapterTest#testIsEmpty;android.widget.cts.AlphabetIndexerTest#testAlphabetIndexer;android.widget.cts.ChronometerTest#testAccessBase;android.widget.cts.TextViewTest#testAccessRawContentType;android.widget.cts.ScrollViewTest#testExecuteKeyEvent;android.widget.cts.SpinnerTest#testAccessPrompt;android.widget.cts.AnalogClockTest#testOnDraw;android.widget.cts.ChronometerTest#testStartAndStop;android.widget.cts.TabWidgetTest#testSetCurrentTab;android.widget.cts.CursorTreeAdapterTest#testGetFilter;android.widget.cts.ImageSwitcherTest#testSetImageURI;android.widget.cts.TabHostTest#testSetOnTabChangedListener;android.widget.cts.ScrollViewTest#testSmoothScrollTo;android.widget.cts.SimpleExpandableListAdapterTest#testGetGroupId;android.widget.cts.AbsListViewTest#testAccessCacheColorHint;android.widget.cts.TabHostTest#testSetCurrentTabByTag;android.widget.cts.HeaderViewListAdapterTest#testIsEnabled;android.widget.cts.ImageViewTest#testSetImageState;android.widget.cts.ExpandableListViewTest#testSetSelectedGroup;android.widget.cts.DialerFilterTest#testRemoveFilterWatcher;android.widget.cts.ToggleButtonTest#testAccessTextOff;android.widget.cts.TextViewTest#testSetSelectAllOnFocus;android.widget.cts.TextViewTest#testAccessKeyListener;android.widget.cts.ArrayAdapterTest#testInsert;android.widget.cts.MediaControllerTest#testSetPrevNextListeners;android.widget.cts.PopupWindowTest#testUpdatePositionAndDimension;android.widget.cts.TextViewTest#testOnDraw;android.widget.cts.TextViewTest#testSetFrame;android.widget.cts.ScrollerTest#testAccessFinalX;android.widget.cts.ScrollerTest#testAccessFinalY;android.widget.cts.AdapterViewTest#testOnLayout;android.widget.cts.ScrollViewTest#testOnRequestFocusInDescendants;android.widget.cts.TextViewTest#testCancelLongPress;android.widget.cts.GridViewTest#testAccessAdapter;android.widget.cts.ZoomButtonTest#testSetZoomSpeed;android.widget.cts.ExpandableListViewTest#testGetPackedPositionGroup;android.widget.cts.ExpandableListViewTest#testSetGroupIndicator;android.widget.cts.TextViewTest#testGetResolvedTextDirectionLtr;android.widget.cts.BaseAdapterTest#testGetDropDownView;android.widget.cts.ViewAnimatorTest#testShowPrevious;android.widget.cts.TextViewTest#testOnCreateContextMenu;android.widget.cts.TextViewTest#testAppend;android.widget.cts.DialerFilterTest#testOnFocusChanged;android.widget.cts.SlidingDrawerTest#testOnLayout;android.widget.cts.TextViewTest#testGetFocusedRect;android.widget.cts.TextSwitcherTest#testAddView;android.widget.cts.ExpandableListViewTest#testSetChildIndicatorBounds;android.widget.cts.RemoteViewsTest#testSetImageViewBitmap;android.widget.cts.CursorTreeAdapterTest#testHasStableIds;android.widget.cts.ToastTest#testConstructor;android.widget.cts.CursorTreeAdapterTest#testConvertToString;android.widget.cts.AbsListViewTest#testGetTopBottomFadingEdgeStrength;android.widget.cts.EditTextTest#testGetDefaultMovementMethod;android.widget.cts.AbsListViewTest#testAddTouchables;android.widget.cts.SeekBarTest#testSetOnSeekBarChangeListener;android.widget.cts.TextViewTest#testAccessLinkTextColor;android.widget.cts.TextViewTest#testAccessEllipsize;android.widget.cts.RatingBarTest#testAccessNumStars;android.widget.cts.ImageViewTest#testGetBaseline;android.widget.cts.AnalogClockTest#testOnAttachedToWindow;android.widget.cts.LayoutDirectionTest#testDirectionInheritanceForAllLayoutsWithCode;android.widget.cts.AutoCompleteTextViewTest#testAccessItemSelectedListener;android.widget.cts.TextSwitcherTest#testConstructor;android.widget.cts.TextViewTest#testGetResolvedTextAlignment;android.widget.cts.ExpandableListView_ExpandableListContextMenuInfoTest#testConstructor;android.widget.cts.HeaderViewListAdapterTest#testGetFilter;android.widget.cts.AbsSeekBarTest#testConstructor;android.widget.cts.ZoomControlsTest#testSetOnZoomOutClickListener;android.widget.cts.ArrayAdapterTest#testSort;android.widget.cts.HorizontalScrollViewTest#testFling;android.widget.cts.GridViewTest#testConstructor;android.widget.cts.RemoteViewsTest#testNotFeasibleSetters;android.widget.cts.RemoteViewsTest#testWriteToParcel;android.widget.cts.ScrollViewTest#testAddView;android.widget.cts.FilterTest#testConstructor;android.widget.cts.SimpleExpandableListAdapterTest#testGetChildId;android.widget.cts.AutoCompleteTextViewTest#testOnDetachedFromWindow;android.widget.cts.ScrollViewTest#testRequestLayout;android.widget.cts.ImageButtonTest#testConstructor;android.widget.cts.EditTextTest#testSelectAll;android.widget.cts.PopupWindowTest#testSetOnDismissListener;android.widget.cts.TimePickerTest#testAccessCurrentHour;android.widget.cts.ArrayAdapterTest#testCreateFromResource;android.widget.cts.TextViewTest#testSetShadowLayer;android.widget.cts.CompoundButtonTest#testOnCreateDrawableState;android.widget.cts.SimpleCursorAdapterTest#testChangeCursorAndColumns;android.widget.cts.ImageViewTest#testSetFrame;android.widget.cts.ArrayAdapterTest#testSetDropDownViewResouce;android.widget.cts.ResourceCursorAdapterTest#testNewDropDownView;android.widget.cts.ToastTest#testCancel;android.widget.cts.RatingBarTest#testAccessStepSize;android.widget.cts.CheckedTextViewTest#testSetCheckMarkDrawableByDrawable;android.widget.cts.TextViewTest#testInstanceState;android.widget.cts.AbsListViewTest#testGetFocusedRect;android.widget.cts.ViewAnimatorTest#testAddView;android.widget.cts.AdapterViewTest#testUnsupportedMethods;android.widget.cts.TextViewTest#testOnPrivateIMECommand;android.widget.cts.CursorAdapterTest#testAccessCursor;android.widget.cts.TextViewTest#testBringPointIntoView;android.widget.cts.SpinnerTest#testOnClick;android.widget.cts.TextViewTest#testSetEditableFactory;android.widget.cts.ViewSwitcherTest#testGetNextView;android.widget.cts.AbsSeekBarTest#testAccessThumbOffset;android.widget.cts.AbsoluteLayout_LayoutParamsTest#testDebug;android.widget.cts.DatePickerTest#testAccessDate;android.widget.cts.CursorAdapterTest#testConstructor;android.widget.cts.HorizontalScrollViewTest#testPageScroll;android.widget.cts.AutoCompleteTextViewTest#testPerformValidation;android.widget.cts.TextViewTest#testAccessLinksClickable;android.widget.cts.SlidingDrawerTest#testGetContent;android.widget.cts.ViewAnimatorTest#testShowNext;android.widget.cts.TextViewTest#testResetTextAlignment;android.widget.cts.ScrollerTest#testFlingMode;android.widget.cts.SlidingDrawerTest#testOnInterceptTouchEvent;android.widget.cts.DatePickerTest#testConstructor;android.widget.cts.TableLayoutTest#testAccessColumnCollapsed;android.widget.cts.AbsSeekBarTest#testVerifyDrawable;android.widget.cts.ScrollViewTest#testOnSizeChanged;android.widget.cts.TimePickerTest#testConstructors;android.widget.cts.TextViewTest#testAccessTransformationMethod;android.widget.cts.SimpleCursorTreeAdapterTest#testBindChildView;android.widget.cts.ScrollerTest#testExtendDuration;android.widget.cts.RatingBarTest#testAccessRating;android.widget.cts.RelativeLayoutTest#testCheckLayoutParams;android.widget.cts.GalleryTest#testGenerateDefaultLayoutParams;android.widget.cts.AbsoluteLayoutTest#testCheckLayoutParams;android.widget.cts.SlidingDrawerTest#testOnFinishInflate;android.widget.cts.EditTextTest#testSetSelectionStartstop;android.widget.cts.ToggleButtonTest#testSetBackgroundDrawable;android.widget.cts.HeaderViewListAdapterTest#testRegisterDataSetObserver;android.widget.cts.TextViewTest#testAccessFreezesText;android.widget.cts.ArrayAdapterTest#testAndroidTestCaseSetupProperly;android.widget.cts.ExpandableListViewTest#testGetSelectedPosition;android.widget.cts.TabHostTest#testGetCurrentTabTag;android.widget.cts.GridViewTest#testOnMeasure;android.widget.cts.HeaderViewListAdapterTest#testGetItemId;android.widget.cts.RadioButtonTest#testToggle;android.widget.cts.TextViewTest#testOnFocusChanged;android.widget.cts.AbsoluteLayout_LayoutParamsTest#testAndroidTestCaseSetupProperly;android.widget.cts.ViewAnimatorTest#testAccessInAnimation;android.widget.cts.AdapterViewTest#testDispatchSaveInstanceState;android.widget.cts.HeaderViewListAdapterTest#testHasStableIds;android.widget.cts.RelativeLayoutTest#testOnMeasure;android.widget.cts.HorizontalScrollViewTest#testSmoothScrollBy;android.widget.cts.ListViewTest#testTransientStateUnstableIds;android.widget.cts.TableRowTest#testConstructor;android.widget.cts.TabHost_TabSpecTest#testSetIndicator2;android.widget.cts.ArrayAdapterTest#testAccessView;android.widget.cts.TabHost_TabSpecTest#testSetIndicator1;android.widget.cts.HorizontalScrollViewTest#testAccessSmoothScrollingEnabled;android.widget.cts.TextViewTest#testSetCursorVisible;android.widget.cts.FrameLayout_LayoutParamsTest#testConstructor;android.widget.cts.AutoCompleteTextViewTest#testOnAttachedToWindow;android.widget.cts.CursorAdapterTest#testInit;android.widget.cts.SimpleAdapterTest#testGetItemId;android.widget.cts.TableLayout_LayoutParamsTest#testSetBaseAttributes;android.widget.cts.ExpandableListViewBasicTest#testExpandedGroupMovement;android.widget.cts.TextViewTest#testAccessPaintFlags;android.widget.cts.CursorAdapterTest#testGetItemId;android.widget.cts.GalleryTest#testSetSpacing;android.widget.cts.TextViewTest#testSetLines;android.widget.cts.ScrollerTest#testConstructor;android.widget.cts.CompoundButtonTest#testOnDraw;android.widget.cts.ListViewTest#testGetMaxScrollAmount;android.widget.cts.TwoLineListItemTest#testGetTexts;android.widget.cts.AbsListViewTest#testBeforeAndAfterTextChanged;android.widget.cts.AdapterViewTest#testConstructor;android.widget.cts.SimpleAdapterTest#testSetViewText;android.widget.cts.ExpandableListViewTest#testGetSelectedId;android.widget.cts.TableLayoutTest#testOnMeasure;android.widget.cts.DialerFilterTest#testSetDigitsWatcher;android.widget.cts.AbsListViewTest#testPointToPosition;android.widget.cts.ExpandableListViewTest#testGetPackedPositionChild;android.widget.cts.ExpandableListViewBasicTest#testSelectedPosition;android.widget.cts.RemoteViewsTest#testSetBitmap;android.widget.cts.TextViewTest#testFoo;android.widget.cts.EditTextTest#testAccessText;android.widget.cts.AbsListViewTest#testComputeVerticalScrollValues;android.widget.cts.VideoViewTest#testConstructor;android.widget.cts.EditTextTest#testGetDefaultEditable;android.widget.cts.EditTextTest#testConstructor;android.widget.cts.ExpandableListViewWithHeadersTest#testExpandOnFirstGroup;android.widget.cts.RelativeLayout_LayoutParamsTest#testStartEnd;android.widget.cts.ScrollViewTest#testAddViewWithIndexAndLayoutParams;android.widget.cts.ViewFlipperTest#testSetFlipInterval;android.widget.cts.ZoomButtonTest#testConstructor;android.widget.cts.TableRowTest#testOnMeasure;android.widget.cts.TableLayoutTest#testAccessStretchAllColumns;android.widget.cts.GridViewTest#testSetHorizontalSpacing;android.widget.cts.ExpandableListViewWithHeadersTest#testContextMenus;android.widget.cts.ListViewTest#testRequestChildRectangleOnScreen;android.widget.cts.VideoViewTest#testSetMediaController;android.widget.cts.AdapterViewTest#testAccessOnItemClickAndLongClickListener;android.widget.cts.RemoteViewsTest#testSetImageViewResource;android.widget.cts.TextViewTest#testScroll;android.widget.cts.VideoViewTest#testResolveAdjustedSize;android.widget.cts.ScrollViewTest#testOnInterceptTouchEvent;android.widget.cts.ViewAnimatorTest#testRemoveViews;android.widget.cts.AlphabetIndexerTest#testAndroidTestCaseSetupProperly;android.widget.cts.ImageViewTest#testSetMaxHeight;android.widget.cts.ImageViewTest#testOnMeasure;android.widget.cts.RadioGroup_LayoutParamsTest#testSetBaseAttributes;android.widget.cts.AbsoluteLayoutTest#testOnMeasure;android.widget.cts.CompoundButtonTest#testPerformClick;android.widget.cts.ExpandableListViewTest#testAccessExpandableListAdapter;android.widget.cts.TextView_SaveStateTest#testToString;android.widget.cts.CursorTreeAdapterTest#testGetGroupView;android.widget.cts.TextViewTest#testPressKey;android.widget.cts.ProgressBarTest#testAccessMax;android.widget.cts.TextViewTest#testBeginEndBatchEdit;android.widget.cts.HeaderViewListAdapterTest#testGetItem;android.widget.cts.LinearLayoutTest#testActivityTestCaseSetUpProperly;android.widget.cts.TextViewTest#testSetGetTextDirection;android.widget.cts.HeaderViewListAdapterTest#testGetView;android.widget.cts.ScrollerTest#testScrollMode;android.widget.cts.ProgressBarTest#testAccessInterpolator;android.widget.cts.TextViewTest#testTextAttr;android.widget.cts.AbsListViewTest#testAccessScrollingCacheEnabled;android.widget.cts.MediaControllerTest#testSetEnabled;android.widget.cts.SimpleCursorAdapterTest#testConvertToString;android.widget.cts.ExpandableListViewTest#testDispatchDraw;android.widget.cts.DigitalClockTest#testOnAttachedToWindow;android.widget.cts.BaseAdapterTest#testGetViewTypeCount;android.widget.cts.CursorAdapterTest#testGetCount;android.widget.cts.ToastTest#testAccessView;android.widget.cts.FrameLayoutTest#testOnMeasure;android.widget.cts.HorizontalScrollViewTest#testGetMaxScrollAmount;android.widget.cts.AbsListViewTest#testSetScrollIndicators;android.widget.cts.TabHostTest#testOnAttachedToAndDetachedFromWindow;android.widget.cts.CursorAdapterTest#testGetFilter;android.widget.cts.CursorTreeAdapterTest#testGetChild;android.widget.cts.SlidingDrawerTest#testSetOnDrawerScrollListener;android.widget.cts.ProgressBarTest#testOnSizeChange;android.widget.cts.TextViewTest#testGetTotalPaddingRight;android.widget.cts.GridViewTest#testPressKey;android.widget.cts.CheckedTextViewTest#testDrawableStateChanged;android.widget.cts.ScrollerTest#testGetDuration;android.widget.cts.TableLayoutTest#testRequestLayout;android.widget.cts.ImageViewTest#testAccessImageMatrix;android.widget.cts.PopupWindowTest#testAccessWidth;android.widget.cts.BaseAdapterTest#testDataSetObserver;android.widget.cts.SpinnerTest#testPerformClick;android.widget.cts.MediaControllerTest#testConstructor;android.widget.cts.SimpleCursorTreeAdapterTest#testConstructor;android.widget.cts.TextViewTest#testGetTextColor;android.widget.cts.DialerFilterTest#testOnModechange;android.widget.cts.AdapterViewTest#testDispatchRestoreInstanceState;android.widget.cts.ImageViewTest#testOnCreateDrawableState;android.widget.cts.CursorTreeAdapterTest#testNotifyDataSetInvalidated;android.widget.cts.SimpleExpandableListAdapterTest#testGetGroupView;android.widget.cts.ListViewTest#testRequestLayout;android.widget.cts.Gallery_LayoutParamsTest#testAndroidTestCaseSetupProperly;android.widget.cts.SimpleExpandableListAdapterTest#testGetChild;android.widget.cts.TableRowTest#testSetOnHierarchyChangeListener;android.widget.cts.TextViewTest#testGetExtendedPaddingTop;android.widget.cts.ResourceCursorAdapterTest#testSetDropDownViewResource;android.widget.cts.LinearLayoutTest#testAccessBaselineAligned;android.widget.cts.CursorTreeAdapterTest#testGetChildId;android.widget.cts.GridViewTest#testSetColumnWidth;android.widget.cts.AbsListViewTest#testAccessTranscriptMode;android.widget.cts.VideoViewTest#testGetBufferPercentage;android.widget.cts.LinearLayoutTest#testConstructor;android.widget.cts.TextViewTest#testAccessContentType;android.widget.cts.SimpleCursorAdapterTest#testBindView;android.widget.cts.SlidingDrawerTest#testOnTouchEvent;android.widget.cts.ListViewTest#testOnKeyUpDown;android.widget.cts.SimpleCursorAdapterTest#testAccessCursorToStringConverter;android.widget.cts.TabWidgetTest#testAddView;android.widget.cts.TextViewTest#testIsInputMethodTarget;android.widget.cts.AbsoluteLayout_LayoutParamsTest#testConstructor;android.widget.cts.RelativeLayoutTest#testGetBaseline;android.widget.cts.TextViewTest#testGetLineCount;android.widget.cts.GridLayoutTest#testActivityTestCaseSetUpProperly;android.widget.cts.ProgressBarTest#testInvalidateDrawable;android.widget.cts.AbsSpinnerTest#testOnSaveAndRestoreInstanceState;android.widget.cts.MultiAutoCompleteTextViewTest#testConstructor;android.widget.cts.ExpandableListViewTest#testGetFlatListPosition;android.widget.cts.ArrayAdapterTest#testAddAllParams;android.widget.cts.CursorTreeAdapterTest#testSetGroupCursor;android.widget.cts.TextViewTest#testDidTouchFocusSelect;android.widget.cts.SimpleCursorAdapterTest#testConstructor;android.widget.cts.TextViewTest#testAccessImeOptions;android.widget.cts.TabHostTest#testClearAllTabs;android.widget.cts.TextViewTest#testGetFadingEdgeStrength;android.widget.cts.TextViewTest#testSetMinLines;android.widget.cts.DialerFilterTest#testConstructor;android.widget.cts.AbsListViewTest#testConstructor;android.widget.cts.HorizontalScrollViewTest#testAddViewWithIndexAndLayoutParams;android.widget.cts.TextViewTest#testTextDirectionDefault;android.widget.cts.TextViewTest#testGetLineBounds;android.widget.cts.HorizontalScrollViewTest#testConstructor;android.widget.cts.AutoCompleteTextViewTest#testSetFrame;android.widget.cts.RelativeLayoutTest#testGenerateLayoutParams2;android.widget.cts.RelativeLayoutTest#testGenerateLayoutParams1;android.widget.cts.ArrayAdapterTest#testAdd;android.widget.cts.PopupWindowTest#testUpdateDimensionAndAlignAnchorView;android.widget.cts.ToastTest#testAccessMargin;android.widget.cts.RemoteViewsActivityTest#testGood;android.widget.cts.TextViewTest#testAccessTextSize;android.widget.cts.TableLayoutTest#testColumnStretchableEffect;android.widget.cts.CompoundButtonTest#testDrawableStateChanged;android.widget.cts.CursorAdapterTest#testAccessFilterQueryProvider;android.widget.cts.HorizontalScrollViewTest#testArrowScroll;android.widget.cts.ScrollViewTest#testOnMeasure;android.widget.cts.TextViewTest#testTextChangedListener;android.widget.cts.AutoCompleteTextViewTest#testSetCompletionHint;android.widget.cts.TextViewTest#testAccessTextColor;android.widget.cts.FrameLayoutTest#testAccessMeasureAllChildren;android.widget.cts.LinearLayoutTest#testAccessWeightSum;android.widget.cts.HorizontalScrollViewTest#testOnMeasure;android.widget.cts.ResourceCursorAdapterTest#testConstructor;android.widget.cts.FrameLayoutTest#testOnSizeChanged;android.widget.cts.LinearLayoutTest#testCheckLayoutParams;android.widget.cts.PopupWindowTest#testSetWindowLayoutMode;android.widget.cts.ExpandableListView_ExpandableListContextMenuInfoTest#testAndroidTestCaseSetupProperly;android.widget.cts.ListViewTest#testAccessHeaderView;android.widget.cts.RatingBarTest#testAccessOnRatingBarChangeListener;android.widget.cts.GridViewTest#testSetGravity;android.widget.cts.TextViewTest#testAccessHint;android.widget.cts.HorizontalScrollViewTest#testOnLayout;android.widget.cts.ListViewTest#testAccessAdapter;android.widget.cts.ArrayAdapterTest#testGetItemId;android.widget.cts.ImageButtonTest#testAndroidTestCaseSetupProperly;android.widget.cts.TextViewTest#testOnDetachedFromWindow;android.widget.cts.ProgressBarTest#testAccessSecondaryProgress;android.widget.cts.SimpleExpandableListAdapterTest#testIsChildSelectable;android.widget.cts.TextViewTest#testTextAlignmentDefault;android.widget.cts.TableLayoutTest#testCheckLayoutParams;android.widget.cts.AnalogClockTest#testOnSizeChanged;android.widget.cts.DialerFilterTest#testSetLettersWatcher;android.widget.cts.ViewAnimatorTest#testAccessDisplayedChildBoundary;android.widget.cts.TextViewTest#testSetSpannableFactory;android.widget.cts.ImageSwitcherTest#testConstructor;android.widget.cts.TextViewTest#testAccessMovementMethod;android.widget.cts.AbsSeekBarTest#testDrawableStateChanged;android.widget.cts.RadioGroupTest#testAddView;android.widget.cts.HorizontalScrollViewTest#testOnInterceptTouchEvent;android.widget.cts.TabHostTest#testConstructor;android.widget.cts.CursorTreeAdapterTest#testGetChildrenCount;android.widget.cts.SimpleCursorAdapterTest#testChangeCursor;android.widget.cts.HorizontalScrollViewTest#testAccessFillViewport;android.widget.cts.AbsSeekBarTest#testFoo;android.widget.cts.TableLayoutTest#testConstructor;android.widget.cts.GalleryTest#testSetUnselectedAlpha;android.widget.cts.AbsListViewTest#testInvalidateViews;android.widget.cts.HeaderViewListAdapterTest#testGetCount;android.widget.cts.RatingBarTest#testAccessIndicator;android.widget.cts.RelativeLayout_LayoutParamsTest#testConstructor;android.widget.cts.CursorAdapterTest#testNewDropDownView;android.widget.cts.ChronometerTest#testAccessFormat;android.widget.cts.ExpandableListViewBasicTest#testPreconditions;android.widget.cts.AbsListViewTest#testFoo;android.widget.cts.ListViewTest#testOnTouchEvent;android.widget.cts.TabHostTest#testAddTab;android.widget.cts.AdapterViewTest#testChangeFocusable;android.widget.cts.ListViewTest#testAccessDivider;android.widget.cts.ListViewTest#testAccessFooterView;android.widget.cts.SimpleCursorAdapterTest#testAccessViewBinder;android.widget.cts.ZoomButtonTest#testSetEnabled;android.widget.cts.CheckedTextViewTest#testConstructor;android.widget.cts.GridViewTest#testLayoutChildren;android.widget.cts.ImageViewTest#testSetAlpha;android.widget.cts.TextViewTest#testClearComposingText;android.widget.cts.ZoomControlsTest#testSetIsZoomInEnabled;android.widget.cts.TabWidgetTest#testConstructor;android.widget.cts.TableRowTest#testGenerateLayoutParams2;android.widget.cts.TextViewTest#testSetTextAppearance;android.widget.cts.FilterTest#testConvertResultToString;android.widget.cts.MultiAutoCompleteTextViewTest#testPerformValidation;android.widget.cts.TextViewTest#testHeightAndWidth;android.widget.cts.CursorTreeAdapterTest#testIsChildSelectable;android.widget.cts.ArrayAdapterTest#testAddAllCollection;android.widget.cts.AbsListView_LayoutParamsTest#testConstructors;android.widget.cts.ScrollViewTest#testDispatchKeyEvent;android.widget.cts.TabHostTest#testAccessCurrentTab;android.widget.cts.ViewSwitcherTest#testReset;android.widget.cts.TimePickerTest#testAccessIs24HourView;android.widget.cts.TextViewTest#testGetTotalPaddingTop;android.widget.cts.HorizontalScrollViewTest#testExecuteKeyEvent;android.widget.cts.AbsoluteLayoutTest#testOnLayout;android.widget.cts.ImageViewTest#testAccessScaleType;android.widget.cts.TabHostTest#testGetTabWidget;android.widget.cts.SlidingDrawerTest#testAnimateToggle;android.widget.cts.SlidingDrawerTest#testLockAndUnlock;android.widget.cts.GalleryTest#testSetGravity;android.widget.cts.ListViewTest#testOnFinishInflate;android.widget.cts.ScrollViewTest#testArrowScroll;android.widget.cts.ScrollViewTest#testComputeScrollDeltaToGetChildRectOnScreen;android.widget.cts.TabHostTest#testGetCurrentTabView;android.widget.cts.ExpandableListViewBasicTest#testExpandGroup;android.widget.cts.TextViewTest#testExtractText;android.widget.cts.RemoteViewsTest#testOnLoadClass;android.widget.cts.RadioGroupTest#testCheckLayoutParams;android.widget.cts.TextSwitcherTest#testSetText;android.widget.cts.AbsListViewTest#testGenerateLayoutParams;android.widget.cts.TabWidgetTest#testSetEnabled;android.widget.cts.AutoCompleteTextViewTest#testAccessItemClickListener;android.widget.cts.ChronometerTest#testFoo;android.widget.cts.ExpandableListViewBasicTest#testConvertionBetweenFlatAndPacked;android.widget.cts.DatePickerTest#testOnSaveInstanceState;android.widget.cts.DatePickerTest#testAndroidTestCaseSetupProperly;android.widget.cts.ImageButtonTest#testOnSetAlpha;android.widget.cts.AdapterViewTest#testAccessOnItemSelectedListener;android.widget.cts.BaseExpandableListAdapterTest#testOnGroupCollapsed;android.widget.cts.TableLayoutTest#testGenerateLayoutParams1;android.widget.cts.TableLayoutTest#testGenerateLayoutParams2;android.widget.cts.LinearLayoutTest#testLayoutVertical;android.widget.cts.CursorTreeAdapterTest#testAccessQueryProvider;android.widget.cts.TextViewTest#testSetTextKeepState1;android.widget.cts.ArrayAdapterTest#testConstructor;android.widget.cts.RemoteViewsTest#testDescribeContents;android.widget.cts.RelativeLayoutTest#testGenerateDefaultLayoutParams;android.widget.cts.SimpleExpandableListAdapterTest#testHasStableIds;android.widget.cts.TextViewTest#testOnPreDraw;android.widget.cts.ExpandableListViewBasicTest#testCollapseGroup;android.widget.cts.ExpandableListViewWithHeadersTest#testExpandOnFirstPosition;android.widget.cts.SimpleCursorAdapterTest#testSetViewText;android.widget.cts.RelativeLayoutTest#testSetIgnoreGravity;android.widget.cts.RadioGroup_LayoutParamsTest#testAndroidTestCaseSetupProperly;android.widget.cts.BaseAdapterTest#testAreAllItemsEnabled;android.widget.cts.RemoteViewsTest#testSetInt;android.widget.cts.AutoCompleteTextViewTest#testAccessDropDownAnchor;android.widget.cts.ScrollerTest#testIsFinished;android.widget.cts.TextViewTest#testAccessGravity;android.widget.cts.TableLayoutTest#testAccessColumnStretchable;android.widget.cts.ExpandableListViewTest#testSetOnGroupClickListener;android.widget.cts.AlphabetIndexerTest#testCompare;android.widget.cts.AbsListView_LayoutParamsTest#testAndroidTestCaseSetupProperly;android.widget.cts.LayoutDirectionTest#testLayoutDirectionDefaults;android.widget.cts.HeaderViewListAdapterTest#testAreAllItemsEnabled;android.widget.cts.LinearLayout_LayoutParamsTest#testDebug;android.widget.cts.CursorTreeAdapterTest#testGetCursor;android.widget.cts.ImageViewTest#testOnDraw;android.widget.cts.GalleryTest#testGetChildDrawingOrder;android.widget.cts.TextViewTest#testAccessInputExtras;android.widget.cts.LinearLayoutTest#testGetBaseline;android.widget.cts.RatingBarTest#testConstructor;android.widget.cts.RemoteViewsTest#testSetUri;android.widget.cts.SpinnerTest#testsetPromptId;android.widget.cts.RemoteViewsTest#testSetTextColor;android.widget.cts.VideoViewTest#testGetDuration;android.widget.cts.AdapterViewTest#testGetCount;android.widget.cts.MultiAutoCompleteTextView_CommaTokenizerTest#testFindTokenStart;android.widget.cts.ImageViewTest#testSetImageBitmap;android.widget.cts.HeaderViewListAdapterTest#testUnregisterDataSetObserver;android.widget.cts.PopupWindowTest#testAccessOutsideTouchable;android.widget.cts.EditTextTest#testSetEllipsize;android.widget.cts.SimpleAdapterTest#testGetCount;android.widget.cts.SpinnerTest#testSetOnItemClickListener;android.widget.cts.SlidingDrawerTest#testToggle;android.widget.cts.AbsSpinnerTest#testPointToPosition;android.widget.cts.ViewFlipperTest#testViewFlipper;android.widget.cts.TabHostTest#testDispatchKeyEvent;android.widget.cts.ExpandableListViewTest#testIsGroupExpanded;android.widget.cts.LayoutDirectionTest#testDirectionFromXml;android.widget.cts.TextViewTest#testVerifyDrawable;android.widget.cts.ProgressBarTest#testIncrementProgressBy;android.widget.cts.PopupWindowTest#testShowAsDropDown;android.widget.cts.ListViewTest#testCanAnimate;android.widget.cts.HorizontalScrollViewTest#testMeasureChild;android.widget.cts.RadioGroupTest#testClearCheck;android.widget.cts.SimpleAdapterTest#testConstructor;android.widget.cts.HorizontalScrollViewTest#testGetHorizontalFadingEdgeStrengths;android.widget.cts.CursorAdapterTest#testGetItem" />
 </TestPlan>
diff --git a/tests/plans/CTS-stable.xml b/tests/plans/CTS-stable.xml
index b9e7321..d9d06d4 100644
--- a/tests/plans/CTS-stable.xml
+++ b/tests/plans/CTS-stable.xml
@@ -31,7 +31,7 @@
   <Entry uri="android.gesture"/>
   <Entry uri="android.graphics"/>
   <Entry uri="android.graphics2"/>
-  <Entry uri="android.hardware" exclude="android.hardware.cts.SensorIntegrationTests#testAccelerometerDoesNotStopGyroscope;android.hardware.cts.SensorIntegrationTests#testsAccelerometerDoesnNotStopMagnetometer;android.hardware.cts.SensorIntegrationTests#testAndroidTestCaseSetupProperly;android.hardware.cts.SensorIntegrationTests#testBatchAndFlush;android.hardware.cts.SensorIntegrationTests#testGyroscopeDoesNotStopAccelerometer;android.hardware.cts.SensorIntegrationTests#testGyroscopeDoesNotStopMagnetometer;android.hardware.cts.SensorIntegrationTests#testMagnetometerDoesNotStopAccelerometer;android.hardware.cts.SensorIntegrationTests#testMagnetometerDoesNotStopGyroscope;android.hardware.cts.SensorMagneticFieldTest#testBatchingStoppingOtherClients;android.hardware.cts.SensorMagneticFieldTest#testBatchingStoppingOtherClientsBatching;android.hardware.cts.SensorMagneticFieldTest#testFrequencyAccuracy;android.hardware.cts.SensorMagneticFieldTest#testOneClientSeveralThreads;android.hardware.cts.SensorMagneticFieldTest#testOneClientSeveralThreadsBatching;android.hardware.cts.SensorMagneticFieldTest#testStandardDeviationWhileStatic;android.hardware.cts.SensorMagneticFieldTest#testStoppingOtherClients;android.hardware.cts.SensorMagneticFieldTest#testStoppingOtherClientsBatching;android.hardware.cts.SensorAccelerometerTest#testBatchingStoppingOtherClients;android.hardware.cts.SensorAccelerometerTest#testBatchingStoppingOtherClientsBatching;android.hardware.cts.SensorAccelerometerTest#testFrequencyAccuracy;android.hardware.cts.SensorAccelerometerTest#testOneClientSeveralThreads;android.hardware.cts.SensorAccelerometerTest#testOneClientSeveralThreadsBatching;android.hardware.cts.SensorGyroscopeTest#testBatchingStoppingOtherClients;android.hardware.cts.SensorGyroscopeTest#testBatchingStoppingOtherClientsBatching;android.hardware.cts.SensorGyroscopeTest#testFrequencyAccuracy;android.hardware.cts.SensorGyroscopeTest#testOneClientSeveralThreads;android.hardware.cts.SensorGyroscopeTest#testOneClientSeveralThreadsBatching;android.hardware.cts.SensorGyroscopeTest#testStandardDeviationWhilStatic;android.hardware.cts.SensorGyroscopeTest#testStoppingOtherClients;android.hardware.cts.SensorGyroscopeTest#testStoppingOtherClientsBatching;android.hardware.cts.SensorAccelerometerTest#testStandardDeviationWhileStatic;android.hardware.cts.SensorAccelerometerTest#testStoppingOtherClients;android.hardware.cts.SensorAccelerometerTest#testStoppingOtherClientsBatching;android.hardware.camera2.cts.CameraDeviceTest#testCameraDeviceRepeatingRequest;android.hardware.camera2.cts.ImageReaderTest#testImageReaderFromCameraJpeg;android.hardware.cts.CameraGLTest#testCameraToSurfaceTextureMetadata;android.hardware.cts.CameraTest#testImmediateZoom;android.hardware.cts.CameraTest#testPreviewCallback;android.hardware.cts.CameraTest#testSmoothZoom;android.hardware.cts.CameraTest#testVideoSnapshot;android.hardware.cts.CameraGLTest#testSetPreviewTextureBothCallbacks;android.hardware.cts.CameraGLTest#testSetPreviewTexturePreviewCallback" />
+  <Entry uri="android.hardware" exclude="android.hardware.camera2.cts.CameraDeviceTest#testCameraDeviceRepeatingRequest;android.hardware.camera2.cts.ImageReaderTest#testImageReaderFromCameraJpeg;android.hardware.cts.CameraGLTest#testCameraToSurfaceTextureMetadata;android.hardware.cts.CameraTest#testImmediateZoom;android.hardware.cts.CameraTest#testPreviewCallback;android.hardware.cts.CameraTest#testSmoothZoom;android.hardware.cts.CameraTest#testVideoSnapshot;android.hardware.cts.CameraGLTest#testSetPreviewTextureBothCallbacks;android.hardware.cts.CameraGLTest#testSetPreviewTexturePreviewCallback" />
   <Entry uri="android.holo"/>
   <Entry uri="android.jni"/>
   <Entry uri="android.keystore"/>
@@ -50,7 +50,7 @@
   <Entry uri="android.permission2"/>
   <Entry uri="android.preference"/>
   <Entry uri="android.preference2"/>
-  <Entry uri="android.provider" exclude="android.provider.cts.CalendarTest#testRemindersAlarm" />
+  <Entry uri="android.provider"/>
   <Entry uri="android.renderscript"/>
   <Entry uri="android.rscpp"/>
   <Entry uri="android.rsg"/>
@@ -59,7 +59,7 @@
   <Entry uri="android.speech"/>
   <Entry uri="android.telephony"/>
   <Entry uri="android.tests.appsecurity"/>
-  <Entry uri="android.tests.location"/>    
+  <Entry uri="android.tests.location"/>
   <Entry uri="android.tests.sigtest"/>
   <Entry uri="android.text"/>
   <Entry uri="android.textureview"/>
@@ -69,7 +69,7 @@
   <Entry uri="android.util"/>
   <Entry uri="android.view"/>
   <Entry uri="android.webkit" exclude="android.webkit.cts.WebViewClientTest#testDoUpdateVisitedHistory;android.webkit.cts.WebViewClientTest#testLoadPage;android.webkit.cts.WebViewClientTest#testOnFormResubmission;android.webkit.cts.WebViewClientTest#testOnReceivedError;android.webkit.cts.WebViewClientTest#testOnReceivedHttpAuthRequest;android.webkit.cts.WebViewClientTest#testOnScaleChanged;android.webkit.cts.WebViewClientTest#testOnUnhandledKeyEvent;android.webkit.cts.WebViewTest#testSetInitialScale" />
-  <Entry uri="android.widget" exclude="android.widget.cts.GridViewTest#testSetNumColumns" />
+  <Entry uri="android.widget"/>
   <Entry uri="com.android.cts.bootup"/>
   <Entry uri="com.android.cts.browserbench"/>
   <Entry uri="com.android.cts.dram"/>
diff --git a/tests/print/Android.mk b/tests/print/Android.mk
new file mode 100644
index 0000000..fea7dc0
--- /dev/null
+++ b/tests/print/Android.mk
@@ -0,0 +1,36 @@
+# Copyright (C) 2014 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)
+
+##################################################
+# Build the print instrument library
+##################################################
+include $(CLEAR_VARS)
+LOCAL_MODULE := CtsPrintInstrument
+LOCAL_SRC_FILES := $(call all-subdir-java-files) \
+    src/android/print/cts/IPrivilegedOperations.aidl
+LOCAL_MODULE_TAGS := optional
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_JAVA_LIBRARY)
+
+# Copy the shell script to run the print instrument Jar to the CTS out folder.
+$(CTS_TESTCASES_OUT)/$(LOCAL_MODULE).jar : $(LOCAL_BUILT_MODULE) | $(ACP) 
+	$(copy-file-to-target)
+
+# Copy the built print instrument library Jar to the CTS out folder.
+$(CTS_TESTCASES_OUT)/print-instrument : $(LOCAL_PATH)/print-instrument | $(ACP)
+	$(copy-file-to-target)
+
diff --git a/tests/print/print-instrument b/tests/print/print-instrument
new file mode 100755
index 0000000..a79cb8a
--- /dev/null
+++ b/tests/print/print-instrument
@@ -0,0 +1,37 @@
+# Copyright (C) 2014 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.
+
+# Script to start "print-instrument" on the device
+#
+# The script sets up an alternative dalvik cache when running as
+# non-root. Jar files needs to be dexopt'd to run in Dalvik. For
+# plain jar files, this is done at first use. shell user does not
+# have write permission to default system Dalvik cache so we
+# redirect to an alternative cache.
+
+RUN_BASE=/data/local/tmp
+
+# If not running as root, use an alternative dex cache.
+if [ ${USER_ID} -ne 0 ]; then
+  tmp_cache=${RUN_BASE}/dalvik-cache
+  if [ ! -d ${tmp_cache} ]; then
+    mkdir -p ${tmp_cache}
+  fi
+  export ANDROID_DATA=${RUN_BASE}
+fi
+
+# Run print-instrument.
+export CLASSPATH=${RUN_BASE}/CtsPrintInstrument.jar
+
+exec app_process ${RUN_BASE} android.print.cts.PrintInstrument ${@}
diff --git a/tests/print/src/android/print/cts/IPrivilegedOperations.aidl b/tests/print/src/android/print/cts/IPrivilegedOperations.aidl
new file mode 100644
index 0000000..93c8c3e
--- /dev/null
+++ b/tests/print/src/android/print/cts/IPrivilegedOperations.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2014 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.print.cts;
+
+interface IPrivilegedOperations {
+    boolean clearApplicationUserData(String packageName);
+}
diff --git a/tests/print/src/android/print/cts/PrintInstrument.java b/tests/print/src/android/print/cts/PrintInstrument.java
new file mode 100644
index 0000000..cd07410
--- /dev/null
+++ b/tests/print/src/android/print/cts/PrintInstrument.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2014 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.print.cts;
+
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
+import android.app.IInstrumentationWatcher;
+import android.app.Instrumentation;
+import android.app.UiAutomationConnection;
+import android.content.ComponentName;
+import android.content.pm.IPackageDataObserver;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.AndroidException;
+import android.view.IWindowManager;
+
+import com.android.internal.os.BaseCommand;
+
+import java.io.PrintStream;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public final class PrintInstrument extends BaseCommand {
+
+    private static final String ARG_PRIVILEGED_OPS = "ARG_PRIVILEGED_OPS";
+
+    private IActivityManager mAm;
+
+    public static void main(String[] args) {
+        PrintInstrument instrumenter = new PrintInstrument();
+        instrumenter.run(args);
+    }
+
+    @Override
+    public void onRun() throws Exception {
+        mAm = ActivityManagerNative.getDefault();
+        if (mAm == null) {
+            System.err.println(NO_SYSTEM_ERROR_CODE);
+            throw new AndroidException("Can't connect to activity manager;"
+                    + " is the system running?");
+        }
+
+        String op = nextArgRequired();
+
+        if (op.equals("instrument")) {
+            runInstrument();
+        } else {
+            showError("Error: unknown command '" + op + "'");
+        }
+    }
+
+    @Override
+    public void onShowUsage(PrintStream out) {
+        /* do nothing */
+    }
+
+    @SuppressWarnings("deprecation")
+    private void runInstrument() throws Exception {
+        String profileFile = null;
+        boolean wait = false;
+        boolean rawMode = false;
+        boolean no_window_animation = false;
+        int userId = UserHandle.USER_CURRENT;
+        Bundle args = new Bundle();
+        String argKey = null, argValue = null;
+        IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
+
+        String opt;
+        while ((opt=nextOption()) != null) {
+            if (opt.equals("-p")) {
+                profileFile = nextArgRequired();
+            } else if (opt.equals("-w")) {
+                wait = true;
+            } else if (opt.equals("-r")) {
+                rawMode = true;
+            } else if (opt.equals("-e")) {
+                argKey = nextArgRequired();
+                argValue = nextArgRequired();
+                args.putString(argKey, argValue);
+            } else if (opt.equals("--no_window_animation")
+                    || opt.equals("--no-window-animation")) {
+                no_window_animation = true;
+            } else if (opt.equals("--user")) {
+                userId = parseUserArg(nextArgRequired());
+            } else {
+                System.err.println("Error: Unknown option: " + opt);
+                return;
+            }
+        }
+
+        if (userId == UserHandle.USER_ALL) {
+            System.err.println("Error: Can't start instrumentation with user 'all'");
+            return;
+        }
+
+        String cnArg = nextArgRequired();
+        ComponentName cn = ComponentName.unflattenFromString(cnArg);
+        if (cn == null) throw new IllegalArgumentException("Bad component name: " + cnArg);
+
+        InstrumentationWatcher watcher = null;
+        UiAutomationConnection connection = null;
+        if (wait) {
+            watcher = new InstrumentationWatcher();
+            watcher.setRawOutput(rawMode);
+            connection = new UiAutomationConnection();
+        }
+
+        float[] oldAnims = null;
+        if (no_window_animation) {
+            oldAnims = wm.getAnimationScales();
+            wm.setAnimationScale(0, 0.0f);
+            wm.setAnimationScale(1, 0.0f);
+        }
+
+        args.putIBinder(ARG_PRIVILEGED_OPS, new PrivilegedOperations(mAm));
+
+        if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId)) {
+            throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());
+        }
+
+        if (watcher != null) {
+            if (!watcher.waitForFinish()) {
+                System.out.println("INSTRUMENTATION_ABORTED: System has crashed.");
+            }
+        }
+
+        if (oldAnims != null) {
+            wm.setAnimationScales(oldAnims);
+        }
+    }
+
+    private int parseUserArg(String arg) {
+        int userId;
+        if ("all".equals(arg)) {
+            userId = UserHandle.USER_ALL;
+        } else if ("current".equals(arg) || "cur".equals(arg)) {
+            userId = UserHandle.USER_CURRENT;
+        } else {
+            userId = Integer.parseInt(arg);
+        }
+        return userId;
+    }
+
+    private class InstrumentationWatcher extends IInstrumentationWatcher.Stub {
+        private boolean mFinished = false;
+        private boolean mRawMode = false;
+
+        /**
+         * Set or reset "raw mode".  In "raw mode", all bundles are dumped.  In "pretty mode",
+         * if a bundle includes Instrumentation.REPORT_KEY_STREAMRESULT, just print that.
+         * @param rawMode true for raw mode, false for pretty mode.
+         */
+        public void setRawOutput(boolean rawMode) {
+            mRawMode = rawMode;
+        }
+
+        @Override
+        public void instrumentationStatus(ComponentName name, int resultCode, Bundle results) {
+            synchronized (this) {
+                // pretty printer mode?
+                String pretty = null;
+                if (!mRawMode && results != null) {
+                    pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT);
+                }
+                if (pretty != null) {
+                    System.out.print(pretty);
+                } else {
+                    if (results != null) {
+                        for (String key : results.keySet()) {
+                            System.out.println(
+                                    "INSTRUMENTATION_STATUS: " + key + "=" + results.get(key));
+                        }
+                    }
+                    System.out.println("INSTRUMENTATION_STATUS_CODE: " + resultCode);
+                }
+                notifyAll();
+            }
+        }
+
+        @Override
+        public void instrumentationFinished(ComponentName name, int resultCode,
+                Bundle results) {
+            synchronized (this) {
+                // pretty printer mode?
+                String pretty = null;
+                if (!mRawMode && results != null) {
+                    pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT);
+                }
+                if (pretty != null) {
+                    System.out.println(pretty);
+                } else {
+                    if (results != null) {
+                        for (String key : results.keySet()) {
+                            System.out.println(
+                                    "INSTRUMENTATION_RESULT: " + key + "=" + results.get(key));
+                        }
+                    }
+                    System.out.println("INSTRUMENTATION_CODE: " + resultCode);
+                }
+                mFinished = true;
+                notifyAll();
+            }
+        }
+
+        public boolean waitForFinish() {
+            synchronized (this) {
+                while (!mFinished) {
+                    try {
+                        if (!mAm.asBinder().pingBinder()) {
+                            return false;
+                        }
+                        wait(1000);
+                    } catch (InterruptedException e) {
+                        throw new IllegalStateException(e);
+                    }
+                }
+            }
+            return true;
+        }
+    }
+
+    private static final class PrivilegedOperations extends IPrivilegedOperations.Stub {
+        private final IActivityManager mAm;
+
+        public PrivilegedOperations(IActivityManager am) {
+            mAm = am;
+        }
+
+        @Override
+        public boolean clearApplicationUserData(final String clearedPackageName)
+                throws RemoteException {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                final AtomicBoolean success = new AtomicBoolean();
+                final CountDownLatch completionLacth = new CountDownLatch(1);
+
+                mAm.clearApplicationUserData(clearedPackageName,
+                        new IPackageDataObserver.Stub() {
+                            @Override
+                            public void onRemoveCompleted(String packageName, boolean succeeded) {
+                                if (clearedPackageName.equals(packageName) && succeeded) {
+                                    success.set(true);
+                                } else {
+                                    success.set(false);
+                                }
+                                completionLacth.countDown();
+                            }
+                }, UserHandle.USER_CURRENT);
+
+                try {
+                    completionLacth.await();
+                } catch (InterruptedException ie) {
+                    /* ignore */
+                }
+
+                return success.get();
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
+}
diff --git a/tests/res/drawable/google_logo_1.png b/tests/res/drawable/google_logo_1.png
new file mode 100644
index 0000000..6e038fc
--- /dev/null
+++ b/tests/res/drawable/google_logo_1.png
Binary files differ
diff --git a/tests/res/drawable/google_logo_2.webp b/tests/res/drawable/google_logo_2.webp
new file mode 100644
index 0000000..f92c42b
--- /dev/null
+++ b/tests/res/drawable/google_logo_2.webp
Binary files differ
diff --git a/tests/res/layout/inflater_override_theme_layout.xml b/tests/res/layout/inflater_override_theme_layout.xml
new file mode 100644
index 0000000..8c524ba
--- /dev/null
+++ b/tests/res/layout/inflater_override_theme_layout.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:theme="@style/Theme_OverrideOuter" >
+
+    <View
+        android:id="@+id/view_outer"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:theme="@style/Theme_OverrideInner" >
+
+        <View
+            android:id="@+id/view_inner"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+
+        <View
+            android:id="@+id/view_attr"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:theme="?attr/themeOverrideAttr" />
+    </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/res/layout/surface_view_2.xml b/tests/res/layout/surface_view_2.xml
new file mode 100644
index 0000000..fe53c71
--- /dev/null
+++ b/tests/res/layout/surface_view_2.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+     Copyright (C) 2014 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <SurfaceView
+        android:id="@+id/surface_view"
+        android:layout_width="320dp"
+        android:layout_height="240dp"/>
+
+</LinearLayout>
diff --git a/tests/res/values/attrs.xml b/tests/res/values/attrs.xml
index 9793893..404427d 100644
--- a/tests/res/values/attrs.xml
+++ b/tests/res/values/attrs.xml
@@ -125,4 +125,8 @@
         <attr name="textColorHint"/>
         <attr name="textColorLink"/>
     </declare-styleable>
+    <!-- Integer used to uniquely identify theme overrides. -->
+    <attr name="themeType" format="integer"/>
+    <!-- Theme reference used to override parent theme. -->
+    <attr name="themeOverrideAttr" format="reference"/>
 </resources>
diff --git a/tests/res/values/styles.xml b/tests/res/values/styles.xml
index f8b56fd..daaed48 100644
--- a/tests/res/values/styles.xml
+++ b/tests/res/values/styles.xml
@@ -14,7 +14,7 @@
      limitations under the License.
 -->
 
-<resources>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
 
     <style name="Whatever">
         <item name="type1">true</item>
@@ -138,4 +138,17 @@
         <item name="android:panelColorBackground">#ffffffff</item>
     </style>
 
+    <style name="Theme_OverrideOuter">
+        <item name="themeType">1</item>
+    </style>
+
+    <style name="Theme_OverrideInner">
+        <item name="themeType">2</item>
+        <item name="themeOverrideAttr">@style/Theme_OverrideAttr</item>
+    </style>
+
+    <style name="Theme_OverrideAttr">
+        <item name="themeType">3</item>
+    </style>
+
 </resources>
diff --git a/tests/src/android/hardware/camera2/cts/Camera2SurfaceViewStubActivity.java b/tests/src/android/hardware/camera2/cts/Camera2SurfaceViewStubActivity.java
new file mode 100644
index 0000000..47b4cba
--- /dev/null
+++ b/tests/src/android/hardware/camera2/cts/Camera2SurfaceViewStubActivity.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2014 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.hardware.camera2.cts;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.ConditionVariable;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import com.android.cts.stub.R;
+
+public class Camera2SurfaceViewStubActivity extends Activity implements SurfaceHolder.Callback {
+    private final static String TAG = "Camera2SurfaceViewStubActivity";
+    private final ConditionVariable surfaceChangedDone = new ConditionVariable();
+
+    private SurfaceView mSurfaceView;
+    private int currentWidth = 0;
+    private int currentHeight = 0;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.surface_view_2);
+        mSurfaceView = (SurfaceView) findViewById(R.id.surface_view);
+        mSurfaceView.getHolder().addCallback(this);
+    }
+
+    public SurfaceView getSurfaceView() {
+        return mSurfaceView;
+    }
+
+    public boolean waitForSurfaceSizeChanged(int timeOutMs, int expectWidth, int expectHeight) {
+        if (timeOutMs <= 0 || expectWidth <= 0 || expectHeight <= 0) {
+            throw new IllegalArgumentException(
+                    String.format(
+                            "timeout(%d), expectWidth(%d), and expectHeight(%d) " +
+                            "should all be positive numbers",
+                            timeOutMs, expectWidth, expectHeight));
+        }
+
+        int waitTimeMs = timeOutMs;
+        boolean changeSucceeded = false;
+        while (!changeSucceeded && waitTimeMs > 0) {
+            long startTimeMs = System.currentTimeMillis();
+            changeSucceeded = surfaceChangedDone.block(waitTimeMs);
+            if (!changeSucceeded) {
+                Log.e(TAG, "Wait for surface change timed out after " + timeOutMs + " ms");
+                return changeSucceeded;
+            } else {
+                // Get a surface change callback, need to check if the size is expected.
+                surfaceChangedDone.close();
+                if (currentWidth == expectWidth && currentHeight == expectHeight) {
+                    return changeSucceeded;
+                }
+                // Do a further iteration surface change check as surfaceChanged could be called
+                // again.
+                changeSucceeded = false;
+            }
+            waitTimeMs -= (System.currentTimeMillis() - startTimeMs);
+        }
+
+        // Couldn't get expected surface size change.
+        return false;
+     }
+
+    @Override
+    public void surfaceCreated(SurfaceHolder holder) {
+    }
+
+    @Override
+    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+        Log.i(TAG, "Surface Changed to: " + width + "x" + height);
+        currentWidth = width;
+        currentHeight = height;
+        surfaceChangedDone.open();
+    }
+
+    @Override
+    public void surfaceDestroyed(SurfaceHolder holder) {
+    }
+}
diff --git a/tests/src/android/webkit/cts/WebViewOnUiThread.java b/tests/src/android/webkit/cts/WebViewOnUiThread.java
index 3616655..715af12 100644
--- a/tests/src/android/webkit/cts/WebViewOnUiThread.java
+++ b/tests/src/android/webkit/cts/WebViewOnUiThread.java
@@ -685,6 +685,15 @@
         });
     }
 
+    public WebView createWebView() {
+        return getValue(new ValueGetter<WebView>() {
+            @Override
+            public WebView capture() {
+                return new WebView(mWebView.getContext());
+            }
+        });
+    }
+
     public PrintDocumentAdapter createPrintDocumentAdapter() {
         return getValue(new ValueGetter<PrintDocumentAdapter>() {
             @Override
diff --git a/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityRecordTest.java b/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityRecordTest.java
index 3aaf54e..7862cb4 100644
--- a/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityRecordTest.java
+++ b/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityRecordTest.java
@@ -204,7 +204,7 @@
     static void assertNoNewNonStaticFieldsAdded(Class<?> clazz, int expectedCount) {
         int nonStaticFieldCount = 0;
 
-        while (clazz != null) {
+        while (clazz != Object.class) {
             for (Field field : clazz.getDeclaredFields()) {
                 if ((field.getModifiers() & Modifier.STATIC) == 0) {
                     nonStaticFieldCount++;
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java
index 91d827c..325b644 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java
@@ -72,6 +72,10 @@
         Config.ARGB_4444};
     private static int[] COLOR_TOLS = new int[] {16, 49, 576};
 
+    private static Config[] COLOR_CONFIGS_RGBA = new Config[] {Config.ARGB_8888,
+        Config.ARGB_4444};
+    private static int[] COLOR_TOLS_RGBA = new int[] {72, 124};
+
     private static int[] RAW_COLORS = new int[] {
         // raw data from R.drawable.premul_data
         Color.argb(255, 0, 0, 0),
@@ -200,11 +204,15 @@
             Bitmap bPng = BitmapFactory.decodeStream(iStreamPng, null, options);
             assertNotNull(bPng);
             assertEquals(bPng.getConfig(), COLOR_CONFIGS[k]);
+            assertFalse(bPng.isPremultiplied());
+            assertFalse(bPng.hasAlpha());
 
             InputStream iStreamWebp1 = obtainInputStream(R.drawable.webp_test);
             Bitmap bWebp1 = BitmapFactory.decodeStream(iStreamWebp1, null, options);
             assertNotNull(bWebp1);
-            compareBitmaps(bPng, bWebp1, COLOR_TOLS[k], true);
+            assertFalse(bWebp1.isPremultiplied());
+            assertFalse(bWebp1.hasAlpha());
+            compareBitmaps(bPng, bWebp1, COLOR_TOLS[k], true, bPng.isPremultiplied());
 
             // Compress the PNG image to WebP format (Quality=90) and decode it back.
             // This will test end-to-end WebP encoding and decoding.
@@ -213,7 +221,46 @@
             InputStream iStreamWebp2 = new ByteArrayInputStream(oStreamWebp.toByteArray());
             Bitmap bWebp2 = BitmapFactory.decodeStream(iStreamWebp2, null, options);
             assertNotNull(bWebp2);
-            compareBitmaps(bPng, bWebp2, COLOR_TOLS[k], true);
+            assertFalse(bWebp2.isPremultiplied());
+            assertFalse(bWebp2.hasAlpha());
+            compareBitmaps(bPng, bWebp2, COLOR_TOLS[k], true, bPng.isPremultiplied());
+        }
+    }
+
+    public void testDecodeStream5() throws IOException {
+        BitmapFactory.Options options = new BitmapFactory.Options();
+        for (int k = 0; k < COLOR_CONFIGS_RGBA.length; ++k) {
+            options.inPreferredConfig = COLOR_CONFIGS_RGBA[k];
+
+            // Decode the PNG & WebP (google_logo) images. The WebP image has
+            // been encoded from PNG image.
+            InputStream iStreamPng = obtainInputStream(R.drawable.google_logo_1);
+            Bitmap bPng = BitmapFactory.decodeStream(iStreamPng, null, options);
+            assertNotNull(bPng);
+            assertEquals(bPng.getConfig(), COLOR_CONFIGS_RGBA[k]);
+            assertTrue(bPng.isPremultiplied());
+            assertTrue(bPng.hasAlpha());
+
+            // Decode the corresponding WebP (transparent) image (google_logo_2.webp).
+            InputStream iStreamWebP1 = obtainInputStream(R.drawable.google_logo_2);
+            Bitmap bWebP1 = BitmapFactory.decodeStream(iStreamWebP1, null, options);
+            assertNotNull(bWebP1);
+            assertEquals(bWebP1.getConfig(), COLOR_CONFIGS_RGBA[k]);
+            assertTrue(bWebP1.isPremultiplied());
+            assertTrue(bWebP1.hasAlpha());
+            compareBitmaps(bPng, bWebP1, COLOR_TOLS_RGBA[k], true, bPng.isPremultiplied());
+
+            // Compress the PNG image to WebP format (Quality=90) and decode it back.
+            // This will test end-to-end WebP encoding and decoding.
+            ByteArrayOutputStream oStreamWebp = new ByteArrayOutputStream();
+            assertTrue(bPng.compress(CompressFormat.WEBP, 90, oStreamWebp));
+            InputStream iStreamWebp2 = new ByteArrayInputStream(oStreamWebp.toByteArray());
+            Bitmap bWebP2 = BitmapFactory.decodeStream(iStreamWebp2, null, options);
+            assertNotNull(bWebP2);
+            assertEquals(bWebP2.getConfig(), COLOR_CONFIGS_RGBA[k]);
+            assertTrue(bWebP2.isPremultiplied());
+            assertTrue(bWebP2.hasAlpha());
+            compareBitmaps(bPng, bWebP2, COLOR_TOLS_RGBA[k], true, bPng.isPremultiplied());
         }
     }
 
@@ -450,7 +497,7 @@
     // lessThanMargin is to indicate whether we expect the mean square error
     // to be "less than" or "no less than".
     private void compareBitmaps(Bitmap expected, Bitmap actual,
-            int mseMargin, boolean lessThanMargin) {
+            int mseMargin, boolean lessThanMargin, boolean isPremultiplied) {
         final int width = expected.getWidth();
         final int height = expected.getHeight();
 
@@ -463,30 +510,49 @@
         int[] expectedColors = new int [width * height];
         int[] actualColors = new int [width * height];
 
+        // Bitmap.getPixels() returns colors with non-premultiplied ARGB values.
         expected.getPixels(expectedColors, 0, width, 0, 0, width, height);
         actual.getPixels(actualColors, 0, width, 0, 0, width, height);
 
         for (int row = 0; row < height; ++row) {
             for (int col = 0; col < width; ++col) {
                 int idx = row * width + col;
-                mse += distance(expectedColors[idx], actualColors[idx]);
+                mse += distance(expectedColors[idx], actualColors[idx], isPremultiplied);
             }
         }
         mse /= width * height;
 
         if (lessThanMargin) {
-            assertTrue("MSE too large for normal case: " + mse,
+            assertTrue("MSE " + mse +  "larger than the threshold: " + mseMargin,
                     mse <= mseMargin);
         } else {
-            assertFalse("MSE too small for abnormal case: " + mse,
+            assertFalse("MSE " + mse +  "smaller than the threshold: " + mseMargin,
                     mse <= mseMargin);
         }
     }
 
-    private double distance(int expect, int actual) {
-        final int r = Color.red(actual) - Color.red(expect);
-        final int g = Color.green(actual) - Color.green(expect);
-        final int b = Color.blue(actual) - Color.blue(expect);
-        return r * r + g * g + b * b;
+    private int multiplyAlpha(int color, int alpha) {
+        return (color * alpha + 127) / 255;
+    }
+
+    // For the Bitmap with Alpha, multiply the Alpha values to get the effective
+    // RGB colors and then compute the color-distance.
+    private double distance(int expect, int actual, boolean isPremultiplied) {
+        if (isPremultiplied) {
+            final int a1 = Color.alpha(actual);
+            final int a2 = Color.alpha(expect);
+            final int r = multiplyAlpha(Color.red(actual), a1) -
+                    multiplyAlpha(Color.red(expect), a2);
+            final int g = multiplyAlpha(Color.green(actual), a1) -
+                    multiplyAlpha(Color.green(expect), a2);
+            final int b = multiplyAlpha(Color.blue(actual), a1) -
+                    multiplyAlpha(Color.blue(expect), a2);
+            return r * r + g * g + b * b;
+        } else {
+            final int r = Color.red(actual) - Color.red(expect);
+            final int g = Color.green(actual) - Color.green(expect);
+            final int b = Color.blue(actual) - Color.blue(expect);
+            return r * r + g * g + b * b;
+        }
     }
 }
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapRegionDecoderTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapRegionDecoderTest.java
index c981db3..5ef710f 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapRegionDecoderTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapRegionDecoderTest.java
@@ -88,7 +88,7 @@
     private int mMseMargin = 3 * (1 * 1);
 
     // MSE margin for WebP Region-Decoding for 'Config.RGB_565' is little bigger.
-    private int mMseMarginWebPConfigRgb565 = 5;
+    private int mMseMarginWebPConfigRgb565 = 8;
 
 
     @Override
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerTest.java
index 0672db6..26cdbb6 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerTest.java
@@ -186,10 +186,10 @@
         assertConstantStateNotSet();
         assertNull(mDrawableContainer.getCurrent());
 
+        mDrawableContainer.setConstantState(mDrawableContainerState);
         mDrawableContainer.setColorFilter(null);
         mDrawableContainer.setColorFilter(new ColorFilter());
 
-        mDrawableContainer.setConstantState(mDrawableContainerState);
         MockDrawable dr = new MockDrawable();
         addAndSelectDrawable(dr);
 
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraCaptureResultTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraCaptureResultTest.java
index 6a708e3..f05111b 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraCaptureResultTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraCaptureResultTest.java
@@ -274,6 +274,7 @@
         ArrayList<CameraMetadata.Key<?>> resultKeys = new ArrayList<CameraMetadata.Key<?>>();
         resultKeys.add(CaptureResult.COLOR_CORRECTION_TRANSFORM);
         resultKeys.add(CaptureResult.COLOR_CORRECTION_GAINS);
+        resultKeys.add(CaptureResult.CONTROL_AE_MODE);
         resultKeys.add(CaptureResult.CONTROL_AE_REGIONS);
         resultKeys.add(CaptureResult.CONTROL_AF_MODE);
         resultKeys.add(CaptureResult.CONTROL_AF_REGIONS);
@@ -302,20 +303,16 @@
         resultKeys.add(CaptureResult.LENS_STATE);
         resultKeys.add(CaptureResult.NOISE_REDUCTION_MODE);
         resultKeys.add(CaptureResult.REQUEST_FRAME_COUNT);
+        resultKeys.add(CaptureResult.REQUEST_PIPELINE_DEPTH);
         resultKeys.add(CaptureResult.SCALER_CROP_REGION);
         resultKeys.add(CaptureResult.SENSOR_EXPOSURE_TIME);
         resultKeys.add(CaptureResult.SENSOR_FRAME_DURATION);
         resultKeys.add(CaptureResult.SENSOR_SENSITIVITY);
         resultKeys.add(CaptureResult.SENSOR_TIMESTAMP);
         resultKeys.add(CaptureResult.SENSOR_TEMPERATURE);
+        resultKeys.add(CaptureResult.SENSOR_TEST_PATTERN_MODE);
         resultKeys.add(CaptureResult.STATISTICS_FACE_DETECT_MODE);
-        resultKeys.add(CaptureResult.STATISTICS_FACE_IDS);
-        resultKeys.add(CaptureResult.STATISTICS_FACE_LANDMARKS);
-        resultKeys.add(CaptureResult.STATISTICS_FACE_RECTANGLES);
-        resultKeys.add(CaptureResult.STATISTICS_FACE_SCORES);
         resultKeys.add(CaptureResult.STATISTICS_LENS_SHADING_MAP);
-        resultKeys.add(CaptureResult.STATISTICS_PREDICTED_COLOR_GAINS);
-        resultKeys.add(CaptureResult.STATISTICS_PREDICTED_COLOR_TRANSFORM);
         resultKeys.add(CaptureResult.STATISTICS_SCENE_FLICKER);
         resultKeys.add(CaptureResult.TONEMAP_CURVE_BLUE);
         resultKeys.add(CaptureResult.TONEMAP_CURVE_GREEN);
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraCharacteristicsTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraCharacteristicsTest.java
index 1b892ba..d694104 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraCharacteristicsTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraCharacteristicsTest.java
@@ -80,6 +80,29 @@
         }
     }
 
+    public void testCameraCharacteristicsAndroidControlAeAvailableModes() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            {
+
+                assertNotNull("Invalid property: android.control.aeAvailableModes",
+                        props.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.control.aeAvailableModes", allKeys.contains(
+                        CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES));
+
+            }
+
+        }
+    }
+
     public void testCameraCharacteristicsAndroidControlAeAvailableTargetFpsRanges() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
@@ -540,6 +563,52 @@
         }
     }
 
+    public void testCameraCharacteristicsAndroidRequestPipelineMaxDepth() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            {
+
+                assertNotNull("Invalid property: android.request.pipelineMaxDepth",
+                        props.get(CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.request.pipelineMaxDepth", allKeys.contains(
+                        CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidRequestPartialResultCount() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            {
+
+                assertNotNull("Invalid property: android.request.partialResultCount",
+                        props.get(CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.request.partialResultCount", allKeys.contains(
+                        CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT));
+
+            }
+
+        }
+    }
+
     public void testCameraCharacteristicsAndroidScalerAvailableFormats() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
@@ -755,6 +824,33 @@
         }
     }
 
+    public void testCameraCharacteristicsAndroidSensorAvailableTestPatternModes() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            Integer hwLevel = props.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
+            assertNotNull("No hardware level reported! android.info.supportedHardwareLevel",
+                    hwLevel);
+            if (hwLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)
+            {
+
+                assertNotNull("Invalid property: android.sensor.availableTestPatternModes",
+                        props.get(CameraCharacteristics.SENSOR_AVAILABLE_TEST_PATTERN_MODES));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.sensor.availableTestPatternModes", allKeys.contains(
+                        CameraCharacteristics.SENSOR_AVAILABLE_TEST_PATTERN_MODES));
+
+            }
+
+        }
+    }
+
     public void testCameraCharacteristicsAndroidSensorInfoActiveArraySize() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
@@ -961,5 +1057,28 @@
 
         }
     }
+
+    public void testCameraCharacteristicsAndroidSyncMaxLatency() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            {
+
+                assertNotNull("Invalid property: android.sync.maxLatency",
+                        props.get(CameraCharacteristics.SYNC_MAX_LATENCY));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.sync.maxLatency", allKeys.contains(
+                        CameraCharacteristics.SYNC_MAX_LATENCY));
+
+            }
+
+        }
+    }
 }
 
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java
index f68b10a..0f05816 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java
@@ -22,7 +22,6 @@
 
 import android.content.Context;
 import android.graphics.ImageFormat;
-import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CameraManager;
 import android.hardware.camera2.CameraMetadata;
@@ -32,6 +31,7 @@
 import android.media.Image;
 import android.media.ImageReader;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.SystemClock;
 import android.test.AndroidTestCase;
 import android.util.Log;
@@ -52,19 +52,11 @@
 
     private CameraManager mCameraManager;
     private BlockingStateListener mCameraListener;
-    private CameraTestThread mLooperThread;
+    private HandlerThread mHandlerThread;
     private Handler mCallbackHandler;
     private int mLatestState = STATE_UNINITIALIZED;
 
-    /**
-     * The error triggered flag starts out as false, and it will flip to true if any errors
-     * are ever caught; it won't be reset to false after that happens. This is due to the
-     * fact that when multiple tests are run back to back (as they are here), it's hard
-     * to associate the asynchronous error with the test that caused it (so we won't even try).
-     */
-    private boolean mErrorTriggered = false;
     private ImageReader mReader;
-    private CameraTestThread mDummyThread;
     private Surface mSurface;
 
     private static final int CAMERA_CONFIGURE_TIMEOUT_MS = 2000;
@@ -120,14 +112,15 @@
 
         mCameraManager = (CameraManager)mContext.getSystemService(Context.CAMERA_SERVICE);
         assertNotNull("Can't connect to camera manager", mCameraManager);
+        mHandlerThread = new HandlerThread(TAG);
+        mHandlerThread.start();
+        mCallbackHandler = new Handler(mHandlerThread.getLooper());
         createDefaultSurface();
-        mLooperThread = new CameraTestThread();
-        mCallbackHandler = mLooperThread.start();
     }
 
     @Override
     protected void tearDown() throws Exception {
-        mDummyThread.close();
+        mHandlerThread.quitSafely();
         mReader.close();
         super.tearDown();
     }
@@ -401,8 +394,7 @@
         mSurface = mReader.getSurface();
         // Create dummy image listener since we don't care the image data in this test.
         ImageReader.OnImageAvailableListener listener = new ImageDropperListener();
-        mDummyThread = new CameraTestThread();
-        mReader.setOnImageAvailableListener(listener, mDummyThread.start());
+        mReader.setOnImageAvailableListener(listener, mCallbackHandler);
     }
 
     private void waitForState(int state, long timeout) {
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java
index 7091cac..d64d47f 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java
@@ -23,6 +23,7 @@
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CameraManager;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
@@ -40,7 +41,7 @@
     private PackageManager mPackageManager;
     private CameraManager mCameraManager;
     private NoopCameraListener mListener;
-    private CameraTestThread mLooperThread;
+    private HandlerThread mHandlerThread;
     private Handler mHandler;
 
     @Override
@@ -57,13 +58,14 @@
     protected void setUp() throws Exception {
         super.setUp();
 
-        mLooperThread = new CameraTestThread();
-        mHandler = mLooperThread.start();
+        mHandlerThread = new HandlerThread(TAG);
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
     }
 
     @Override
     protected void tearDown() throws Exception {
-        mLooperThread.close();
+        mHandlerThread.quitSafely();
         mHandler = null;
 
         super.tearDown();
@@ -252,12 +254,9 @@
      * a listener that isn't registered should have no effect.
      */
     public void testCameraManagerListener() throws Exception {
-        CameraTestThread callbackThread = new CameraTestThread();
-        Handler callbackHandler = callbackThread.start();
-
         mCameraManager.removeAvailabilityListener(mListener);
-        mCameraManager.addAvailabilityListener(mListener, callbackHandler);
-        mCameraManager.addAvailabilityListener(mListener, callbackHandler);
+        mCameraManager.addAvailabilityListener(mListener, mHandler);
+        mCameraManager.addAvailabilityListener(mListener, mHandler);
         mCameraManager.removeAvailabilityListener(mListener);
         mCameraManager.removeAvailabilityListener(mListener);
     }
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraSurfaceViewPreviewTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraSurfaceViewPreviewTest.java
new file mode 100644
index 0000000..1e3764e
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraSurfaceViewPreviewTest.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2014 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.hardware.camera2.cts;
+
+import static android.hardware.camera2.cts.CameraTestUtils.*;
+import static com.android.ex.camera2.blocking.BlockingStateListener.*;
+
+import android.content.Context;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraDevice.CaptureListener;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CaptureFailure;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.Size;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import android.view.Surface;
+
+import com.android.ex.camera2.blocking.BlockingStateListener;
+
+import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
+import static org.mockito.Mockito.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * CameraDevice capture use case tests, including preview, still capture, burst
+ * capture etc.
+ */
+public class CameraSurfaceViewPreviewTest
+        extends ActivityInstrumentationTestCase2<Camera2SurfaceViewStubActivity> {
+    // Can not use class name exactly as it exceed the log tag character limit.
+    private static final String TAG = "CameraPreviewTest";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+    private static final int WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS = 1000;
+    private static final int FRAME_TIMEOUT_MS = 500;
+    private static final int NUM_FRAMES_VERIFIED = 30;
+
+    private Context mContext;
+    private CameraManager mCameraManager;
+    private String[] mCameraIds;
+    private CameraDevice mCamera;
+    private HandlerThread mHandlerThread;
+    private Handler mHandler;
+    private BlockingStateListener mCameraListener;
+
+    public CameraSurfaceViewPreviewTest() {
+        super(Camera2SurfaceViewStubActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mContext = getActivity();
+        mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
+        assertNotNull("Unable to get CameraManager", mCameraManager);
+        mCameraIds = mCameraManager.getCameraIdList();
+        assertNotNull("Unable to get camera ids", mCameraIds);
+        mHandlerThread = new HandlerThread(TAG);
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
+        mCameraListener = new BlockingStateListener();
+
+        /**
+         * Workaround for mockito and JB-MR2 incompatibility
+         *
+         * Avoid java.lang.IllegalArgumentException: dexcache == null
+         * https://code.google.com/p/dexmaker/issues/detail?id=2
+         */
+        System.setProperty("dexmaker.dexcache", mContext.getCacheDir().toString());
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mHandlerThread.quitSafely();
+        mHandler = null;
+        mCameraListener = null;
+        super.tearDown();
+    }
+
+    public void testCameraPreview() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                Log.i(TAG, "Testing preview for Camera " + mCameraIds[i]);
+                mCamera = openCamera(mCameraManager, mCameraIds[i], mCameraListener, mHandler);
+                assertNotNull(
+                        String.format("Failed to open camera device ID: %s", mCameraIds[i]),
+                        mCamera);
+                previewTestByCamera();
+            } finally {
+                if (mCamera != null) {
+                    mCamera.close();
+                    mCamera = null;
+                }
+            }
+        }
+    }
+
+    /**
+     * Test all supported preview sizes for a camera device
+     *
+     * @throws Exception
+     */
+    private void previewTestByCamera() throws Exception {
+        List<Size> previewSizes = getSupportedPreviewSizes(
+                mCamera.getId(), mCameraManager, PREVIEW_SIZE_BOUND);
+        Camera2SurfaceViewStubActivity stubActivity = getActivity();
+
+        for (final Size sz : previewSizes) {
+            if (VERBOSE) {
+                Log.v(TAG, "Testing camera preview size: " + sz.toString());
+            }
+            // Change the preview size
+            final SurfaceHolder holder = stubActivity.getSurfaceView().getHolder();
+            Handler handler = new Handler(Looper.getMainLooper());
+            handler.post(new Runnable() {
+                @Override
+                public void run() {
+                    holder.setFixedSize(sz.getWidth(), sz.getHeight());
+                }
+            });
+
+            boolean res = stubActivity.waitForSurfaceSizeChanged(
+                    WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS, sz.getWidth(), sz.getHeight());
+            assertTrue("wait for surface change to " + sz.toString() + " timed out", res);
+            Surface previewSurface = holder.getSurface();
+            assertTrue("Preview surface is invalid", previewSurface.isValid());
+
+            CaptureListener mockCaptureListener =
+                    mock(CameraDevice.CaptureListener.class);
+
+            startPreview(previewSurface, mockCaptureListener);
+
+            verifyCaptureResults(mCamera, mockCaptureListener, NUM_FRAMES_VERIFIED,
+                    NUM_FRAMES_VERIFIED * FRAME_TIMEOUT_MS);
+
+            stopPreview();
+        }
+    }
+
+    private void startPreview(Surface surface, CaptureListener listener) throws Exception {
+        List<Surface> outputSurfaces = new ArrayList<Surface>(/*capacity*/1);
+        outputSurfaces.add(surface);
+        mCamera.configureOutputs(outputSurfaces);
+        mCameraListener.waitForState(STATE_BUSY, CAMERA_BUSY_TIMEOUT_MS);
+        mCameraListener.waitForState(STATE_IDLE, CAMERA_IDLE_TIMEOUT_MS);
+
+        // TODO: vary the different settings like crop region to cover more cases.
+        CaptureRequest.Builder captureBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+
+        captureBuilder.addTarget(surface);
+        mCamera.setRepeatingRequest(captureBuilder.build(), listener, mHandler);
+    }
+
+    private void stopPreview() throws Exception {
+        if (VERBOSE) Log.v(TAG, "Stopping preview and waiting for idle");
+        // Stop repeat, wait for captures to complete, and disconnect from surfaces
+        mCamera.configureOutputs(/*outputs*/ null);
+        mCameraListener.waitForState(STATE_BUSY, CAMERA_BUSY_TIMEOUT_MS);
+        mCameraListener.waitForState(STATE_UNCONFIGURED, CAMERA_IDLE_TIMEOUT_MS);
+    }
+
+    private class IsCaptureResultValid extends ArgumentMatcher<CaptureResult> {
+        @Override
+        public boolean matches(Object obj) {
+            CaptureResult result = (CaptureResult)obj;
+            Long timeStamp = result.get(CaptureResult.SENSOR_TIMESTAMP);
+            if (timeStamp != null && timeStamp.longValue() > 0L) {
+                return true;
+            }
+            return false;
+        }
+    }
+
+    private void verifyCaptureResults(
+            CameraDevice camera,
+            CameraDevice.CaptureListener mockListener,
+            int expectResultCount,
+            int timeOutMs) {
+        // Should receive expected number of onCaptureStarted callbacks.
+        ArgumentCaptor<Long> timestamps = ArgumentCaptor.forClass(Long.class);
+        verify(mockListener,
+                timeout(timeOutMs).atLeast(expectResultCount))
+                        .onCaptureStarted(
+                                eq(camera),
+                                isA(CaptureRequest.class),
+                                timestamps.capture());
+
+        // Validate timestamps: all timestamps should be larger than 0 and monotonically increase.
+        long timestamp = 0;
+        for (Long nextTimestamp : timestamps.getAllValues()) {
+            Log.v(TAG, "next t: " + nextTimestamp + " current t: " + timestamp);
+            assertTrue("Captures are out of order", timestamp < nextTimestamp);
+            timestamp = nextTimestamp;
+        }
+
+        // Should receive expected number of capture results.
+        verify(mockListener,
+                timeout(timeOutMs).atLeast(expectResultCount))
+                        .onCaptureCompleted(
+                                eq(camera),
+                                isA(CaptureRequest.class),
+                                argThat(new IsCaptureResultValid()));
+
+        // Should not receive any capture failed callbacks.
+        verify(mockListener, never())
+                        .onCaptureFailed(
+                                eq(camera),
+                                isA(CaptureRequest.class),
+                                isA(CaptureFailure.class));
+    }
+
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestThread.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestThread.java
deleted file mode 100644
index 9516ead..0000000
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestThread.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.camera2.cts;
-
-import android.os.ConditionVariable;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.Log;
-
-import java.util.concurrent.TimeoutException;
-
-/**
- * Camera test thread wrapper for handling camera callbacks
- */
-public class CameraTestThread implements AutoCloseable {
-    private static final String TAG = "CameraTestThread";
-    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
-    // Timeout for initializing looper and opening camera in Milliseconds.
-    private static final long WAIT_FOR_COMMAND_TO_COMPLETE = 5000;
-    private Looper mLooper = null;
-    private Handler mHandler = null;
-
-    /**
-     * Create and start a looper thread, return the Handler
-     */
-    public synchronized Handler start() throws Exception {
-        final ConditionVariable startDone = new ConditionVariable();
-        if (mLooper != null || mHandler !=null) {
-            Log.w(TAG, "Looper thread already started");
-            return mHandler;
-        }
-
-        new Thread() {
-            @Override
-            public void run() {
-                if (VERBOSE) Log.v(TAG, "start loopRun");
-                Looper.prepare();
-                // Save the looper so that we can terminate this thread
-                // after we are done with it.
-                mLooper = Looper.myLooper();
-                mHandler = new Handler();
-                startDone.open();
-                Looper.loop();
-                if (VERBOSE) Log.v(TAG, "createLooperThread: finished");
-            }
-        }.start();
-
-        if (VERBOSE) Log.v(TAG, "start waiting for looper");
-        if (!startDone.block(WAIT_FOR_COMMAND_TO_COMPLETE)) {
-            throw new TimeoutException("createLooperThread: start timeout");
-        }
-        return mHandler;
-    }
-
-    /**
-     * Terminate the looper thread
-     */
-    public synchronized void close() throws Exception {
-        if (mLooper == null || mHandler == null) {
-            Log.w(TAG, "Looper thread doesn't start yet");
-            return;
-        }
-
-        if (VERBOSE) Log.v(TAG, "Terminate looper thread");
-        mLooper.quit();
-        mLooper.getThread().join();
-        mLooper = null;
-        mHandler = null;
-    }
-
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            close();
-        } finally {
-            super.finalize();
-        }
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
index 7f10cb8..779cb47 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -38,7 +38,11 @@
 import java.io.IOException;
 import java.lang.reflect.Array;
 import java.nio.ByteBuffer;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
 
 /**
  * A package private utility class for wrapping up the camera2 cts test common utility functions
@@ -47,11 +51,14 @@
     private static final String TAG = "CameraTestUtils";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
 
+    // Only test the preview and video size that is no larger than 1080p.
+    public static final Size PREVIEW_SIZE_BOUND = new Size(1920, 1080);
     // Default timeouts for reaching various states
-    public static final int CAMERA_OPEN_TIMEOUT_MS = 500;
+    public static final int CAMERA_OPEN_TIMEOUT_MS = 2000;
+    public static final int CAMERA_CLOSE_TIMEOUT_MS = 2000;
     public static final int CAMERA_IDLE_TIMEOUT_MS = 2000;
-    public static final int CAMERA_ACTIVE_TIMEOUT_MS = 500;
-    public static final int CAMERA_BUSY_TIMEOUT_MS = 500;
+    public static final int CAMERA_ACTIVE_TIMEOUT_MS = 1000;
+    public static final int CAMERA_BUSY_TIMEOUT_MS = 1000;
 
     public static class ImageDropperListener implements ImageReader.OnImageAvailableListener {
         @Override
@@ -287,7 +294,53 @@
                         String.format("Invalid format specified 0x%x", format));
         }
         Size[] availableSizes = properties.get(key);
+        assertArrayNotEmpty(availableSizes, "availableSizes should not be empty");
         if (VERBOSE) Log.v(TAG, "Supported sizes are: " + Arrays.deepToString(availableSizes));
         return availableSizes;
     }
-}
\ No newline at end of file
+
+    /**
+     * Size comparator that compares the number of pixels it covers.
+     */
+    public static class SizeComparator implements Comparator<Size> {
+        @Override
+        public int compare(Size lhs, Size rhs) {
+            long left = lhs.getWidth() * lhs.getHeight();
+            long right = rhs.getWidth() * rhs.getHeight();
+            return (left < right) ? -1 : (left > right ? 1 : 0);
+        }
+    }
+
+    /**
+     * Get sorted size list in descending order. Remove the sizes larger than
+     * the bound. If the bound is null, don't do the size bound filtering.
+     */
+    static public List<Size> getSupportedPreviewSizes(
+            String cameraId, CameraManager cameraManager, Size bound) throws Exception {
+        Comparator<Size> comparator = new SizeComparator();
+        Size[] sizes = getSupportedSizeForFormat(ImageFormat.YUV_420_888, cameraId, cameraManager);
+        List<Size> supportedPreviewSizes = null;
+        if (bound != null) {
+            supportedPreviewSizes = new ArrayList<Size>(/* capacity */1);
+            for (Size sz : sizes) {
+                if (comparator.compare(sz, bound) <= 0) {
+                    supportedPreviewSizes.add(sz);
+                }
+            }
+        } else {
+            supportedPreviewSizes = Arrays.asList(sizes);
+        }
+        assertTrue("Supported preview size should have at least one element",
+                supportedPreviewSizes.size() > 0);
+
+        Collections.sort(supportedPreviewSizes, comparator);
+        // Make it in descending order.
+        Collections.reverse(supportedPreviewSizes);
+        return supportedPreviewSizes;
+    }
+
+    static public List<Size> getSupportedVideoSizes(
+            String cameraId, CameraManager cameraManager, Size bound) throws Exception {
+        return getSupportedPreviewSizes(cameraId, cameraManager, bound);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
index 28cb13e..211b5d3 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
@@ -32,6 +32,7 @@
 import android.media.ImageReader;
 import android.os.Environment;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.test.AndroidTestCase;
 import android.util.Log;
 import android.view.Surface;
@@ -69,10 +70,10 @@
     private CameraDevice mCamera;
     private BlockingStateListener mCameraListener;
     private String[] mCameraIds;
-    private ImageReader mReader = null;
-    private Handler mHandler = null;
-    private SimpleImageListener mListener = null;
-    private CameraTestThread mLooperThread = null;
+    private ImageReader mReader;
+    private Handler mHandler;
+    private SimpleImageListener mListener;
+    private HandlerThread mHandlerThread;
 
     @Override
     public void setContext(Context context) {
@@ -85,8 +86,9 @@
     protected void setUp() throws Exception {
         super.setUp();
         mCameraIds = mCameraManager.getCameraIdList();
-        mLooperThread = new CameraTestThread();
-        mHandler = mLooperThread.start();
+        mHandlerThread = new HandlerThread(TAG);
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
         mCameraListener = new BlockingStateListener();
     }
 
@@ -100,7 +102,7 @@
             mReader.close();
             mReader = null;
         }
-        mLooperThread.close();
+        mHandlerThread.quitSafely();
         mHandler = null;
         super.tearDown();
     }
@@ -110,7 +112,7 @@
             Log.i(TAG, "Testing Camera " + mCameraIds[i]);
             openDevice(mCameraIds[i]);
             bufferFormatTestByCamera(ImageFormat.YUV_420_888, mCameraIds[i]);
-            closeDevice(mCameraIds[i]);
+            closeDevice();
         }
     }
 
@@ -119,7 +121,7 @@
             Log.v(TAG, "Testing Camera " + mCameraIds[i]);
             openDevice(mCameraIds[i]);
             bufferFormatTestByCamera(ImageFormat.JPEG, mCameraIds[i]);
-            closeDevice(mCameraIds[i]);
+            closeDevice();
         }
     }
 
@@ -136,16 +138,8 @@
         CameraCharacteristics properties = mCameraManager.getCameraCharacteristics(cameraId);
         assertNotNull("Can't get camera properties!", properties);
 
-        /**
-         * TODO: cleanup the color format mess, we probably need define formats
-         * in Image class instead of using ImageFormat for camera. also,
-         * probably make sense to change the available format type from Enum[]
-         * to int[]. It'll also be nice to put this into a helper function and
-         * move to util class.
-         */
         int[] availableFormats = properties.get(CameraCharacteristics.SCALER_AVAILABLE_FORMATS);
-        assertArrayNotEmpty(availableFormats,
-                "availableFormats should not be empty");
+        assertArrayNotEmpty(availableFormats, "availableFormats should not be empty");
         Arrays.sort(availableFormats);
         assertTrue("Can't find the format " + format + " in supported formats " +
                 Arrays.toString(availableFormats),
@@ -160,7 +154,7 @@
 
             prepareImageReader(sz, format);
 
-            CaptureRequest request = prepareCaptureRequest(format);
+            CaptureRequest request = prepareCaptureRequest();
 
             captureAndValidateImage(request, sz, format);
 
@@ -216,7 +210,7 @@
         if (VERBOSE) Log.v(TAG, "Preparing ImageReader size " + sz.toString());
     }
 
-    private CaptureRequest prepareCaptureRequest(int format) throws Exception {
+    private CaptureRequest prepareCaptureRequest() throws Exception {
         List<Surface> outputSurfaces = new ArrayList<Surface>(1);
         Surface surface = mReader.getSurface();
         assertNotNull("Fail to get surface from ImageReader", surface);
@@ -282,6 +276,7 @@
         if (mCamera != null) {
             throw new IllegalStateException("Already have open camera device");
         }
+
         try {
             mCamera = CameraTestUtils.openCamera(
                 mCameraManager, cameraId, mCameraListener, mHandler);
@@ -292,11 +287,11 @@
             mCamera = null;
             fail("Fail to open camera, " + Log.getStackTraceString(e));
         }
-        mCameraListener.waitForState(STATE_UNCONFIGURED, CAMERA_OPEN_TIMEOUT_MS);
     }
 
-    private void closeDevice(String cameraId) {
+    private void closeDevice() {
         mCamera.close();
+        mCameraListener.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS);
         mCamera = null;
     }
 
@@ -335,7 +330,7 @@
                 BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length));
         if (DUMP_FILE) {
             String fileName =
-                    DEBUG_FILE_NAME_BASE + width + "x" + height + ".yuv";
+                    DEBUG_FILE_NAME_BASE + "/" + width + "x" + height + ".jpeg";
             dumpFile(fileName, jpegData);
         }
     }
diff --git a/tests/tests/hardware/src/android/hardware/cts/CameraTest.java b/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
index b198541..ec0977e 100644
--- a/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
@@ -32,6 +32,7 @@
 import android.media.CamcorderProfile;
 import android.media.ExifInterface;
 import android.media.MediaRecorder;
+import android.os.Build;
 import android.os.ConditionVariable;
 import android.os.Environment;
 import android.os.Looper;
@@ -62,7 +63,7 @@
 public class CameraTest extends ActivityInstrumentationTestCase2<CameraStubActivity> {
     private static String TAG = "CameraTest";
     private static final String PACKAGE = "com.android.cts.stub";
-    private static final boolean LOGV = false;
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
     private final String JPEG_PATH = Environment.getExternalStorageDirectory().getPath() +
             "/test.jpg";
     private byte[] mJpegData;
@@ -90,6 +91,13 @@
     private static final int AUTOEXPOSURE_LOCK = 0;
     private static final int AUTOWHITEBALANCE_LOCK = 1;
 
+    // Some exif tags that are not defined by ExifInterface but supported.
+    private static final String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
+    private static final String TAG_SUBSEC_TIME = "SubSecTime";
+    private static final String TAG_SUBSEC_TIME_ORIG = "SubSecTimeOriginal";
+    private static final String TAG_SUBSEC_TIME_DIG = "SubSecTimeDigitized";
+
+
     private PreviewCallback mPreviewCallback = new PreviewCallback();
     private TestShutterCallback mShutterCallback = new TestShutterCallback();
     private RawPictureCallback mRawPictureCallback = new RawPictureCallback();
@@ -107,7 +115,7 @@
 
     public CameraTest() {
         super(PACKAGE, CameraStubActivity.class);
-        if (LOGV) Log.v(TAG, "Camera Constructor");
+        if (VERBOSE) Log.v(TAG, "Camera Constructor");
     }
 
     @Override
@@ -150,7 +158,7 @@
                 Log.v(TAG, "camera is opened");
                 startDone.open();
                 Looper.loop(); // Blocks forever until Looper.quit() is called.
-                if (LOGV) Log.v(TAG, "initializeMessageLooper: quit.");
+                if (VERBOSE) Log.v(TAG, "initializeMessageLooper: quit.");
             }
         }.start();
 
@@ -186,7 +194,7 @@
     private static int calculateBufferSize(int width, int height,
                                            int format, int bpp) {
 
-        if (LOGV) {
+        if (VERBOSE) {
             Log.v(TAG, "calculateBufferSize: w=" + width + ",h=" + height
             + ",f=" + format + ",bpp=" + bpp);
         }
@@ -203,7 +211,7 @@
             int c_size = c_stride * height/2;
             int size = y_size + c_size * 2;
 
-            if (LOGV) {
+            if (VERBOSE) {
                 Log.v(TAG, "calculateBufferSize: YV12 size= " + size);
             }
 
@@ -237,9 +245,9 @@
             }
             mPreviewCallbackResult = PREVIEW_CALLBACK_RECEIVED;
             mCamera.stopPreview();
-            if (LOGV) Log.v(TAG, "notify the preview callback");
+            if (VERBOSE) Log.v(TAG, "notify the preview callback");
             mPreviewDone.open();
-            if (LOGV) Log.v(TAG, "Preview callback stop");
+            if (VERBOSE) Log.v(TAG, "Preview callback stop");
         }
     }
 
@@ -247,7 +255,7 @@
     private final class TestShutterCallback implements ShutterCallback {
         public void onShutter() {
             mShutterCallbackResult = true;
-            if (LOGV) Log.v(TAG, "onShutter called");
+            if (VERBOSE) Log.v(TAG, "onShutter called");
         }
     }
 
@@ -255,7 +263,7 @@
     private final class RawPictureCallback implements PictureCallback {
         public void onPictureTaken(byte [] rawData, Camera camera) {
             mRawPictureCallbackResult = true;
-            if (LOGV) Log.v(TAG, "RawPictureCallback callback");
+            if (VERBOSE) Log.v(TAG, "RawPictureCallback callback");
         }
     }
 
@@ -272,14 +280,14 @@
                     outStream.close();
                     mJpegPictureCallbackResult = true;
 
-                    if (LOGV) {
+                    if (VERBOSE) {
                         Log.v(TAG, "JpegPictureCallback rawDataLength = " + rawData.length);
                     }
                 } else {
                     mJpegPictureCallbackResult = false;
                 }
                 mSnapshotDone.open();
-                if (LOGV) Log.v(TAG, "Jpeg Picture callback");
+                if (VERBOSE) Log.v(TAG, "Jpeg Picture callback");
             } catch (IOException e) {
                 // no need to fail here; callback worked fine
                 Log.w(TAG, "Error writing picture to sd card.");
@@ -312,7 +320,7 @@
     }
 
     private void waitForPreviewDone() {
-        if (LOGV) Log.v(TAG, "Wait for preview callback");
+        if (VERBOSE) Log.v(TAG, "Wait for preview callback");
         if (!mPreviewDone.block(WAIT_FOR_COMMAND_TO_COMPLETE)) {
             // timeout could be expected or unexpected. The caller will decide.
             Log.v(TAG, "waitForPreviewDone: timeout");
@@ -339,7 +347,18 @@
     }
 
     private void checkPreviewCallback() throws Exception {
-        if (LOGV) Log.v(TAG, "check preview callback");
+        if (VERBOSE) Log.v(TAG, "check preview callback");
+        mCamera.startPreview();
+        waitForPreviewDone();
+        mCamera.setPreviewCallback(null);
+    }
+
+    /**
+     * Start preview and wait for the first preview callback, which indicates the
+     * preview becomes active.
+     */
+    private void blockingStartPreview() {
+        mCamera.setPreviewCallback(new SimplePreviewStreamCb(/*Id*/0));
         mCamera.startPreview();
         waitForPreviewDone();
         mCamera.setPreviewCallback(null);
@@ -384,7 +403,7 @@
             assertEquals(pictureSize.height, bmpOptions.outHeight);
         } else {
             int realArea = bmpOptions.outWidth * bmpOptions.outHeight;
-            if (LOGV) Log.v(TAG, "Video snapshot is " +
+            if (VERBOSE) Log.v(TAG, "Video snapshot is " +
                     bmpOptions.outWidth + " x " + bmpOptions.outHeight +
                     ", video size is " + videoWidth + " x " + videoHeight);
             assertTrue ("Video snapshot too small! Expected at least " +
@@ -809,6 +828,21 @@
         assertTrue(mJpegPictureCallbackResult);
         exif = new ExifInterface(JPEG_PATH);
         assertFalse(exif.hasThumbnail());
+        // Primary image should still be valid for no thumbnail capture.
+        BitmapFactory.decodeFile(JPEG_PATH, bmpOptions);
+        if (!recording) {
+            assertTrue("Jpeg primary image size should match requested size",
+                    bmpOptions.outWidth == pictureSize.width &&
+                    bmpOptions.outHeight == pictureSize.height);
+        } else {
+            assertTrue(bmpOptions.outWidth >= recordingWidth ||
+                    bmpOptions.outWidth == size.width);
+            assertTrue(bmpOptions.outHeight >= recordingHeight ||
+                    bmpOptions.outHeight == size.height);
+        }
+
+        assertNotNull("Jpeg primary image data should be decodable",
+                BitmapFactory.decodeFile(JPEG_PATH));
     }
 
     @UiThreadTest
@@ -834,8 +868,10 @@
 
         // Test various exif tags.
         ExifInterface exif = new ExifInterface(JPEG_PATH);
-        assertNotNull(exif.getAttribute(ExifInterface.TAG_MAKE));
-        assertNotNull(exif.getAttribute(ExifInterface.TAG_MODEL));
+        StringBuffer failedCause = new StringBuffer("Jpeg exif test failed:\n");
+        boolean extraExiftestPassed = checkExtraExifTagsSucceeds(failedCause, exif);
+
+        if (VERBOSE) Log.v(TAG, "Testing exif tag TAG_DATETIME");
         String datetime = exif.getAttribute(ExifInterface.TAG_DATETIME);
         assertNotNull(datetime);
         // Datetime should be local time.
@@ -848,6 +884,7 @@
         assertBitmapAndJpegSizeEqual(mJpegData, exif);
 
         // Test gps exif tags.
+        if (VERBOSE) Log.v(TAG, "Testing exif GPS tags");
         testGpsExifValues(parameters, 37.736071, -122.441983, 21, 1199145600,
             "GPS NETWORK HYBRID ARE ALL FINE.");
         testGpsExifValues(parameters, 0.736071, 0.441983, 1, 1199145601, "GPS");
@@ -855,6 +892,7 @@
 
         // Test gps tags do not exist after calling removeGpsData. Also check if
         // image width and height exif match the jpeg when jpeg rotation is set.
+        if (VERBOSE) Log.v(TAG, "Testing exif GPS tag removal");
         if (!recording) mCamera.startPreview();
         parameters.removeGpsData();
         parameters.setRotation(90); // For testing image width and height exif.
@@ -864,11 +902,164 @@
         exif = new ExifInterface(JPEG_PATH);
         checkGpsDataNull(exif);
         assertBitmapAndJpegSizeEqual(mJpegData, exif);
+        assertTrue(failedCause.toString(), extraExiftestPassed);
         // Reset the rotation to prevent from affecting other tests.
         parameters.setRotation(0);
         mCamera.setParameters(parameters);
     }
 
+    /**
+     * Sanity check of some extra exif tags.
+     * <p>
+     * Sanity check some extra exif tags without asserting the check failures
+     * immediately. When a failure is detected, the failure cause is logged,
+     * the rest of the tests are still executed. The caller can assert with the
+     * failure cause based on the returned test status.
+     * </p>
+     *
+     * @param logBuf Log failure cause to this StringBuffer if there is
+     * any failure.
+     * @param exif The exif data associated with a jpeg image being tested.
+     * @return true if no test failure is found, false if there is any failure.
+     */
+    private boolean checkExtraExifTagsSucceeds(StringBuffer logBuf, ExifInterface exif) {
+        if (logBuf == null || exif == null) {
+            throw new IllegalArgumentException("failureCause and exif shouldn't be null");
+        }
+
+        if (VERBOSE) Log.v(TAG, "Testing extra exif tags");
+        boolean allTestsPassed = true;
+        boolean passedSoFar = true;
+        String failureMsg;
+
+        // TAG_EXPOSURE_TIME
+        // ExifInterface API gives exposure time value in the form of float instead of rational
+        String exposureTime = exif.getAttribute(ExifInterface.TAG_EXPOSURE_TIME);
+        passedSoFar = expectNotNull("Exif TAG_EXPOSURE_TIME is null!", logBuf, exposureTime);
+        if (passedSoFar) {
+            double exposureTimeValue = Double.parseDouble(exposureTime);
+            failureMsg = "Exif exposure time " + exposureTime + " should be a positive value";
+            passedSoFar = expectTrue(failureMsg, logBuf, exposureTimeValue > 0);
+        }
+        allTestsPassed = allTestsPassed && passedSoFar;
+
+        // TAG_APERTURE
+        // ExifInterface API gives aperture value in the form of float instead of rational
+        String aperture = exif.getAttribute(ExifInterface.TAG_APERTURE);
+        passedSoFar = expectNotNull("Exif TAG_APERTURE is null!", logBuf, aperture);
+        if (passedSoFar) {
+            double apertureValue = Double.parseDouble(aperture);
+            passedSoFar = expectTrue("Exif TAG_APERTURE value " + aperture + " should be positive!",
+                    logBuf, apertureValue > 0);
+        }
+        allTestsPassed = allTestsPassed && passedSoFar;
+
+        // TAG_FLASH
+        String flash = exif.getAttribute(ExifInterface.TAG_FLASH);
+        passedSoFar = expectNotNull("Exif TAG_FLASH is null!", logBuf, flash);
+        allTestsPassed = allTestsPassed && passedSoFar;
+
+        // TAG_WHITE_BALANCE
+        String whiteBalance = exif.getAttribute(ExifInterface.TAG_WHITE_BALANCE);
+        passedSoFar = expectNotNull("Exif TAG_WHITE_BALANCE is null!", logBuf, whiteBalance);
+        allTestsPassed = allTestsPassed && passedSoFar;
+
+        // TAG_MAKE
+        String make = exif.getAttribute(ExifInterface.TAG_MAKE);
+        passedSoFar = expectNotNull("Exif TAG_MAKE is null!", logBuf, make);
+        if (passedSoFar) {
+            passedSoFar = expectTrue("Exif TAG_MODEL value: " + make
+                    + " should match build manufacturer: " + Build.MANUFACTURER, logBuf,
+                    make.equals(Build.MANUFACTURER));
+        }
+        allTestsPassed = allTestsPassed && passedSoFar;
+
+        // TAG_MODEL
+        String model = exif.getAttribute(ExifInterface.TAG_MODEL);
+        passedSoFar = expectNotNull("Exif TAG_MODEL is null!", logBuf, model);
+        if (passedSoFar) {
+            passedSoFar = expectTrue("Exif TAG_MODEL value: " + model
+                    + " should match build manufacturer: " + Build.MODEL, logBuf,
+                    model.equals(Build.MODEL));
+        }
+        allTestsPassed = allTestsPassed && passedSoFar;
+
+        // TAG_ISO
+        int iso = exif.getAttributeInt(ExifInterface.TAG_ISO, -1);
+        passedSoFar = expectTrue("Exif ISO value " + iso + " is invalid", logBuf, iso > 0);
+        allTestsPassed = allTestsPassed && passedSoFar;
+
+        // TAG_DATETIME_DIGITIZED (a.k.a Create time for digital cameras).
+        String digitizedTime = exif.getAttribute(TAG_DATETIME_DIGITIZED);
+        passedSoFar = expectNotNull("Exif TAG_DATETIME_DIGITIZED is null!", logBuf, digitizedTime);
+        if (passedSoFar) {
+            String datetime = exif.getAttribute(ExifInterface.TAG_DATETIME);
+            passedSoFar = expectNotNull("Exif TAG_DATETIME is null!", logBuf, datetime);
+            if (passedSoFar) {
+                passedSoFar = expectTrue("dataTime should match digitizedTime", logBuf,
+                        digitizedTime.equals(datetime));
+            }
+        }
+        allTestsPassed = allTestsPassed && passedSoFar;
+
+        /**
+         * TAG_SUBSEC_TIME. Since the sub second tag strings are truncated to at
+         * most 9 digits in ExifInterface implementation, use getAttributeInt to
+         * sanitize it. When the default value -1 is returned, it means that
+         * this exif tag either doesn't exist or is a non-numerical invalid
+         * string. Same rule applies to the rest of sub second tags.
+         */
+        int subSecTime = exif.getAttributeInt(TAG_SUBSEC_TIME, -1);
+        passedSoFar = expectTrue(
+                "Exif TAG_SUBSEC_TIME value is null or invalid!", logBuf, subSecTime > 0);
+        allTestsPassed = allTestsPassed && passedSoFar;
+
+        // TAG_SUBSEC_TIME_ORIG
+        int subSecTimeOrig = exif.getAttributeInt(TAG_SUBSEC_TIME_ORIG, -1);
+        passedSoFar = expectTrue(
+                "Exif TAG_SUBSEC_TIME_ORIG value is null or invalid!", logBuf, subSecTimeOrig > 0);
+        allTestsPassed = allTestsPassed && passedSoFar;
+
+        // TAG_SUBSEC_TIME_DIG
+        int subSecTimeDig = exif.getAttributeInt(TAG_SUBSEC_TIME_DIG, -1);
+        passedSoFar = expectTrue(
+                "Exif TAG_SUBSEC_TIME_DIG value is null or invalid!", logBuf, subSecTimeDig > 0);
+        allTestsPassed = allTestsPassed && passedSoFar;
+
+        return allTestsPassed;
+    }
+
+    /**
+     * Check if object is null and log failure msg.
+     *
+     * @param msg Failure msg.
+     * @param logBuffer StringBuffer to log the failure msg.
+     * @param obj Object to test.
+     * @return true if object is not null, otherwise return false.
+     */
+    private boolean expectNotNull(String msg, StringBuffer logBuffer, Object obj)
+    {
+        if (obj == null) {
+            logBuffer.append(msg + "\n");
+        }
+        return (obj != null);
+    }
+
+    /**
+     * Check if condition is false and log failure msg.
+     *
+     * @param msg Failure msg.
+     * @param logBuffer StringBuffer to log the failure msg.
+     * @param condition Condition to test.
+     * @return The value of the condition.
+     */
+    private boolean expectTrue(String msg, StringBuffer logBuffer, boolean condition) {
+        if (!condition) {
+            logBuffer.append(msg + "\n");
+        }
+        return condition;
+    }
+
     private void assertBitmapAndJpegSizeEqual(byte[] jpegData, ExifInterface exif) {
         int exifWidth = exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, 0);
         int exifHeight = exif.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, 0);
@@ -1152,8 +1343,7 @@
             for (int i = 0; i < ratios.size() - 1; i++) {
                 assertTrue(ratios.get(i) < ratios.get(i + 1));
             }
-            mCamera.startPreview();
-            waitForPreviewDone();
+            blockingStartPreview();
 
             // Test each zoom step.
             for (int i = 0; i <= maxZoom; i++) {
@@ -1316,8 +1506,8 @@
 
     private void testFocusDistancesByCamera(int cameraId) throws Exception {
         initializeMessageLooper(cameraId);
-        mCamera.startPreview();
-        waitForPreviewDone();
+        blockingStartPreview();
+
         Parameters parameters = mCamera.getParameters();
 
         // Test every supported focus mode.
@@ -1785,7 +1975,7 @@
                 double intervalMargin = 0.9;
                 long lastArrivalTime = mFrames.get(mFrames.size() - 1);
                 double interval = arrivalTime - lastArrivalTime;
-                if (LOGV) Log.v(TAG, "Frame interval=" + interval);
+                if (VERBOSE) Log.v(TAG, "Frame interval=" + interval);
                 try {
                     assertTrue("Frame interval (" + interval + "ms) is too " +
                             "large. mMaxFrameInterval=" +
@@ -1890,8 +2080,7 @@
 
             // Make sure scene mode settings are consistent before preview and
             // after preview.
-            mCamera.startPreview();
-            waitForPreviewDone();
+            blockingStartPreview();
             for (int i = 0; i < supportedSceneModes.size(); i++) {
                 String sceneMode = supportedSceneModes.get(i);
                 parameters.setSceneMode(sceneMode);
@@ -2064,7 +2253,7 @@
     @UiThreadTest
     public void testMultiCameraRelease() throws Exception {
         // Verify that multiple cameras exist, and that they can be opened at the same time
-        if (LOGV) Log.v(TAG, "testMultiCameraRelease: Checking pre-conditions.");
+        if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Checking pre-conditions.");
         int nCameras = Camera.getNumberOfCameras();
         if (nCameras < 2) {
             Log.i(TAG, "Test multi-camera release: Skipping test because only 1 camera available");
@@ -2086,11 +2275,11 @@
         testCamera1.release();
 
         // Start first camera
-        if (LOGV) Log.v(TAG, "testMultiCameraRelease: Opening camera 0");
+        if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Opening camera 0");
         initializeMessageLooper(0);
         SimplePreviewStreamCb callback0 = new SimplePreviewStreamCb(0);
         mCamera.setPreviewCallback(callback0);
-        if (LOGV) Log.v(TAG, "testMultiCameraRelease: Starting preview on camera 0");
+        if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Starting preview on camera 0");
         mCamera.startPreview();
         // Run preview for a bit
         for (int f = 0; f < 100; f++) {
@@ -2098,7 +2287,7 @@
             assertTrue("testMultiCameraRelease: First camera preview timed out on frame " + f + "!",
                        mPreviewDone.block( WAIT_FOR_COMMAND_TO_COMPLETE));
         }
-        if (LOGV) Log.v(TAG, "testMultiCameraRelease: Stopping preview on camera 0");
+        if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Stopping preview on camera 0");
         mCamera.stopPreview();
         // Save message looper and camera to deterministically release them, instead
         // of letting GC do it at some point.
@@ -2110,11 +2299,11 @@
 
         // Start second camera without releasing the first one (will
         // set mCamera and mLooper to new objects)
-        if (LOGV) Log.v(TAG, "testMultiCameraRelease: Opening camera 1");
+        if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Opening camera 1");
         initializeMessageLooper(1);
         SimplePreviewStreamCb callback1 = new SimplePreviewStreamCb(1);
         mCamera.setPreviewCallback(callback1);
-        if (LOGV) Log.v(TAG, "testMultiCameraRelease: Starting preview on camera 1");
+        if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Starting preview on camera 1");
         mCamera.startPreview();
         // Run preview for a bit - GC of first camera instance should not impact the second's
         // operation.
@@ -2124,11 +2313,11 @@
                        mPreviewDone.block( WAIT_FOR_COMMAND_TO_COMPLETE));
             if (f == 50) {
                 // Release first camera mid-preview, should cause no problems
-                if (LOGV) Log.v(TAG, "testMultiCameraRelease: Releasing camera 0");
+                if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Releasing camera 0");
                 firstCamera.release();
             }
         }
-        if (LOGV) Log.v(TAG, "testMultiCameraRelease: Stopping preview on camera 0");
+        if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Stopping preview on camera 0");
         mCamera.stopPreview();
 
         firstLooper.quit();
@@ -2144,7 +2333,7 @@
             mId = id;
         }
         public void onPreviewFrame(byte[] data, android.hardware.Camera camera) {
-            if (LOGV) Log.v(TAG, "Preview frame callback, id " + mId + ".");
+            if (VERBOSE) Log.v(TAG, "Preview frame callback, id " + mId + ".");
             mPreviewDone.open();
         }
     }
diff --git a/tests/tests/media/res/raw/big5_1.mp3 b/tests/tests/media/res/raw/big5_1.mp3
new file mode 100644
index 0000000..faa3eb4
--- /dev/null
+++ b/tests/tests/media/res/raw/big5_1.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/big5_2.mp3 b/tests/tests/media/res/raw/big5_2.mp3
new file mode 100644
index 0000000..a69da4f
--- /dev/null
+++ b/tests/tests/media/res/raw/big5_2.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/cp1251_3.mp3 b/tests/tests/media/res/raw/cp1251_3.mp3
new file mode 100644
index 0000000..179a1a5
--- /dev/null
+++ b/tests/tests/media/res/raw/cp1251_3.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/cp1251_4.mp3 b/tests/tests/media/res/raw/cp1251_4.mp3
new file mode 100644
index 0000000..3df1d32
--- /dev/null
+++ b/tests/tests/media/res/raw/cp1251_4.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/cp1251_5.mp3 b/tests/tests/media/res/raw/cp1251_5.mp3
new file mode 100644
index 0000000..46df442
--- /dev/null
+++ b/tests/tests/media/res/raw/cp1251_5.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/cp1251_6.mp3 b/tests/tests/media/res/raw/cp1251_6.mp3
new file mode 100644
index 0000000..545834d
--- /dev/null
+++ b/tests/tests/media/res/raw/cp1251_6.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/cp1251_v1.mp3 b/tests/tests/media/res/raw/cp1251_v1.mp3
new file mode 100644
index 0000000..173d970
--- /dev/null
+++ b/tests/tests/media/res/raw/cp1251_v1.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/cp1251_v1v2.mp3 b/tests/tests/media/res/raw/cp1251_v1v2.mp3
new file mode 100644
index 0000000..abffa92
--- /dev/null
+++ b/tests/tests/media/res/raw/cp1251_v1v2.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/gb18030_1.mp3 b/tests/tests/media/res/raw/gb18030_1.mp3
new file mode 100644
index 0000000..dc63de5
--- /dev/null
+++ b/tests/tests/media/res/raw/gb18030_1.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/gb18030_2.mp3 b/tests/tests/media/res/raw/gb18030_2.mp3
new file mode 100644
index 0000000..6109c97
--- /dev/null
+++ b/tests/tests/media/res/raw/gb18030_2.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/gb18030_3.mp3 b/tests/tests/media/res/raw/gb18030_3.mp3
new file mode 100644
index 0000000..4fcb22f
--- /dev/null
+++ b/tests/tests/media/res/raw/gb18030_3.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/gb18030_4.mp3 b/tests/tests/media/res/raw/gb18030_4.mp3
new file mode 100644
index 0000000..fedffd7
--- /dev/null
+++ b/tests/tests/media/res/raw/gb18030_4.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/gb18030_5.mp3 b/tests/tests/media/res/raw/gb18030_5.mp3
new file mode 100644
index 0000000..70f76ce
--- /dev/null
+++ b/tests/tests/media/res/raw/gb18030_5.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/gb18030_6.mp3 b/tests/tests/media/res/raw/gb18030_6.mp3
new file mode 100644
index 0000000..b4817b2
--- /dev/null
+++ b/tests/tests/media/res/raw/gb18030_6.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/gb18030_7.mp3 b/tests/tests/media/res/raw/gb18030_7.mp3
new file mode 100644
index 0000000..7932596
--- /dev/null
+++ b/tests/tests/media/res/raw/gb18030_7.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/gb18030_8.mp3 b/tests/tests/media/res/raw/gb18030_8.mp3
new file mode 100644
index 0000000..f5f54de
--- /dev/null
+++ b/tests/tests/media/res/raw/gb18030_8.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/hebrew.mp3 b/tests/tests/media/res/raw/hebrew.mp3
new file mode 100644
index 0000000..59d76d8
--- /dev/null
+++ b/tests/tests/media/res/raw/hebrew.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/hebrew2.mp3 b/tests/tests/media/res/raw/hebrew2.mp3
new file mode 100644
index 0000000..d48cad2
--- /dev/null
+++ b/tests/tests/media/res/raw/hebrew2.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/iso88591_1.ogg b/tests/tests/media/res/raw/iso88591_1.ogg
new file mode 100644
index 0000000..c20bf34
--- /dev/null
+++ b/tests/tests/media/res/raw/iso88591_1.ogg
Binary files differ
diff --git a/tests/tests/media/res/raw/shiftjis1.mp3 b/tests/tests/media/res/raw/shiftjis1.mp3
new file mode 100644
index 0000000..1c50c76
--- /dev/null
+++ b/tests/tests/media/res/raw/shiftjis1.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/shiftjis2.mp3 b/tests/tests/media/res/raw/shiftjis2.mp3
new file mode 100644
index 0000000..808c597
--- /dev/null
+++ b/tests/tests/media/res/raw/shiftjis2.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/shiftjis3.mp3 b/tests/tests/media/res/raw/shiftjis3.mp3
new file mode 100644
index 0000000..820631b
--- /dev/null
+++ b/tests/tests/media/res/raw/shiftjis3.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/shiftjis4.mp3 b/tests/tests/media/res/raw/shiftjis4.mp3
new file mode 100644
index 0000000..3fbc25e
--- /dev/null
+++ b/tests/tests/media/res/raw/shiftjis4.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/shiftjis5.mp3 b/tests/tests/media/res/raw/shiftjis5.mp3
new file mode 100644
index 0000000..90520f8
--- /dev/null
+++ b/tests/tests/media/res/raw/shiftjis5.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/shiftjis6.mp3 b/tests/tests/media/res/raw/shiftjis6.mp3
new file mode 100644
index 0000000..5310936
--- /dev/null
+++ b/tests/tests/media/res/raw/shiftjis6.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/shiftjis7.mp3 b/tests/tests/media/res/raw/shiftjis7.mp3
new file mode 100644
index 0000000..6143126
--- /dev/null
+++ b/tests/tests/media/res/raw/shiftjis7.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/shiftjis8.mp3 b/tests/tests/media/res/raw/shiftjis8.mp3
new file mode 100644
index 0000000..c45c130
--- /dev/null
+++ b/tests/tests/media/res/raw/shiftjis8.mp3
Binary files differ
diff --git a/tests/tests/media/src/android/media/cts/AudioTrackTest.java b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
index 9b1879a..12b98b5 100644
--- a/tests/tests/media/src/android/media/cts/AudioTrackTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
@@ -446,10 +446,9 @@
     // Playback properties
     // ----------------------------------
 
-    // Test case 1: setStereoVolume() with max volume returns SUCCESS
-    public void testSetStereoVolumeMax() throws Exception {
+    // Common code for the testSetStereoVolume* and testSetVolume* tests
+    private void testSetVolumeCommon(String testName, float vol, boolean isStereo) throws Exception {
         // constants for test
-        final String TEST_NAME = "testSetStereoVolumeMax";
         final int TEST_SR = 22050;
         final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_STEREO;
         final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
@@ -465,61 +464,35 @@
         track.write(data, OFFSET_DEFAULT, data.length);
         track.write(data, OFFSET_DEFAULT, data.length);
         track.play();
-        float maxVol = AudioTrack.getMaxVolume();
-        assertTrue(TEST_NAME, track.setStereoVolume(maxVol, maxVol) == AudioTrack.SUCCESS);
+        if (isStereo) {
+            // TODO to really test this, do a pan instead of using same value for left and right
+            assertTrue(testName, track.setStereoVolume(vol, vol) == AudioTrack.SUCCESS);
+        } else {
+            assertTrue(testName, track.setVolume(vol) == AudioTrack.SUCCESS);
+        }
         // -------- tear down --------------
         track.release();
     }
 
+    // Test case 1: setStereoVolume() with max volume returns SUCCESS
+    public void testSetStereoVolumeMax() throws Exception {
+        final String TEST_NAME = "testSetStereoVolumeMax";
+        float maxVol = AudioTrack.getMaxVolume();
+        testSetVolumeCommon(TEST_NAME, maxVol, true /*isStereo*/);
+    }
+
     // Test case 2: setStereoVolume() with min volume returns SUCCESS
     public void testSetStereoVolumeMin() throws Exception {
-        // constants for test
         final String TEST_NAME = "testSetStereoVolumeMin";
-        final int TEST_SR = 22050;
-        final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_STEREO;
-        final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
-        final int TEST_MODE = AudioTrack.MODE_STREAM;
-        final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
-
-        // -------- initialization --------------
-        int minBuffSize = AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT);
-        AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT,
-                2 * minBuffSize, TEST_MODE);
-        byte data[] = new byte[minBuffSize];
-        // -------- test --------------
-        track.write(data, OFFSET_DEFAULT, data.length);
-        track.write(data, OFFSET_DEFAULT, data.length);
-        track.play();
         float minVol = AudioTrack.getMinVolume();
-        assertTrue(TEST_NAME, track.setStereoVolume(minVol, minVol) == AudioTrack.SUCCESS);
-        // -------- tear down --------------
-        track.release();
+        testSetVolumeCommon(TEST_NAME, minVol, true /*isStereo*/);
     }
 
     // Test case 3: setStereoVolume() with mid volume returns SUCCESS
     public void testSetStereoVolumeMid() throws Exception {
-        // constants for test
         final String TEST_NAME = "testSetStereoVolumeMid";
-        final int TEST_SR = 22050;
-        final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_STEREO;
-        final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
-        final int TEST_MODE = AudioTrack.MODE_STREAM;
-        final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
-
-        // -------- initialization --------------
-        int minBuffSize = AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT);
-        AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT,
-                2 * minBuffSize, TEST_MODE);
-        byte data[] = new byte[minBuffSize];
-        // -------- test --------------
-
-        track.write(data, OFFSET_DEFAULT, data.length);
-        track.write(data, OFFSET_DEFAULT, data.length);
-        track.play();
         float midVol = (AudioTrack.getMaxVolume() - AudioTrack.getMinVolume()) / 2;
-        assertTrue(TEST_NAME, track.setStereoVolume(midVol, midVol) == AudioTrack.SUCCESS);
-        // -------- tear down --------------
-        track.release();
+        testSetVolumeCommon(TEST_NAME, midVol, true /*isStereo*/);
     }
 
     // Test case 4: setPlaybackRate() with half the content rate returns SUCCESS
@@ -644,6 +617,27 @@
         track.release();
     }
 
+    // Test case 9: setVolume() with max volume returns SUCCESS
+    public void testSetVolumeMax() throws Exception {
+        final String TEST_NAME = "testSetVolumeMax";
+        float maxVol = AudioTrack.getMaxVolume();
+        testSetVolumeCommon(TEST_NAME, maxVol, false /*isStereo*/);
+    }
+
+    // Test case 10: setVolume() with min volume returns SUCCESS
+    public void testSetVolumeMin() throws Exception {
+        final String TEST_NAME = "testSetVolumeMin";
+        float minVol = AudioTrack.getMinVolume();
+        testSetVolumeCommon(TEST_NAME, minVol, false /*isStereo*/);
+    }
+
+    // Test case 11: setVolume() with mid volume returns SUCCESS
+    public void testSetVolumeMid() throws Exception {
+        final String TEST_NAME = "testSetVolumeMid";
+        float midVol = (AudioTrack.getMaxVolume() - AudioTrack.getMinVolume()) / 2;
+        testSetVolumeCommon(TEST_NAME, midVol, false /*isStereo*/);
+    }
+
     // -----------------------------------------------------------------
     // Playback progress
     // ----------------------------------
diff --git a/tests/tests/media/src/android/media/cts/DecodeEditEncodeTest.java b/tests/tests/media/src/android/media/cts/DecodeEditEncodeTest.java
index 0d83647..e74a64a 100644
--- a/tests/tests/media/src/android/media/cts/DecodeEditEncodeTest.java
+++ b/tests/tests/media/src/android/media/cts/DecodeEditEncodeTest.java
@@ -149,7 +149,8 @@
     /**
      * Tests editing of a video file with GL.
      */
-    private void videoEditTest() {
+    private void videoEditTest()
+            throws IOException {
         VideoChunks sourceChunks = new VideoChunks();
 
         if (!generateVideoFile(sourceChunks)) {
@@ -182,7 +183,8 @@
      *
      * @return true on success, false on "soft" failure
      */
-    private boolean generateVideoFile(VideoChunks output) {
+    private boolean generateVideoFile(VideoChunks output)
+            throws IOException {
         if (VERBOSE) Log.d(TAG, "generateVideoFile " + mWidth + "x" + mHeight);
         MediaCodec encoder = null;
         InputSurface inputSurface = null;
@@ -393,7 +395,8 @@
      * for output and a Surface for input, we can avoid issues with obscure formats and can
      * use a fragment shader to do transformations.
      */
-    private VideoChunks editVideoFile(VideoChunks inputData) {
+    private VideoChunks editVideoFile(VideoChunks inputData)
+            throws IOException {
         if (VERBOSE) Log.d(TAG, "editVideoFile " + mWidth + "x" + mHeight);
         VideoChunks outputData = new VideoChunks();
         MediaCodec decoder = null;
@@ -613,7 +616,8 @@
      * Checks the video file to see if the contents match our expectations.  We decode the
      * video to a Surface and check the pixels with GL.
      */
-    private void checkVideoFile(VideoChunks inputData) {
+    private void checkVideoFile(VideoChunks inputData)
+            throws IOException {
         OutputSurface surface = null;
         MediaCodec decoder = null;
 
diff --git a/tests/tests/media/src/android/media/cts/DecoderTest.java b/tests/tests/media/src/android/media/cts/DecoderTest.java
index f250c3d..c4c8465 100644
--- a/tests/tests/media/src/android/media/cts/DecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/DecoderTest.java
@@ -33,6 +33,7 @@
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.zip.CRC32;
 
 public class DecoderTest extends MediaPlayerTestBase {
@@ -42,6 +43,11 @@
     private static final int RESET_MODE_RECONFIGURE = 1;
     private static final int RESET_MODE_FLUSH = 2;
 
+    private static final String[] CSD_KEYS = new String[] { "csd-0", "csd-1" };
+
+    private static final int CONFIG_MODE_NONE = 0;
+    private static final int CONFIG_MODE_QUEUE = 1;
+
     private Resources mResources;
     short[] mMasterBuffer;
 
@@ -69,6 +75,20 @@
         masterFd.close();
     }
 
+    // TODO: add similar tests for other audio and video formats
+    public void testBug11696552() throws Exception {
+        MediaCodec mMediaCodec = MediaCodec.createDecoderByType("audio/mp4a-latm");
+        MediaFormat mFormat = MediaFormat.createAudioFormat("audio/mp4a-latm", 48000, 2);
+        mFormat.setByteBuffer("csd-0", ByteBuffer.wrap( new byte [] {0x13, 0x10} ));
+        mFormat.setInteger(MediaFormat.KEY_IS_ADTS, 1);
+        mMediaCodec.configure(mFormat, null, null, 0);
+        mMediaCodec.start();
+        int index = mMediaCodec.dequeueInputBuffer(250000);
+        mMediaCodec.queueInputBuffer(index, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM );
+        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+        mMediaCodec.dequeueOutputBuffer(info, 250000);
+    }
+
     // The allowed errors in the following tests are the actual maximum measured
     // errors with the standard decoders, plus 10%.
     // This should allow for some variation in decoders, while still detecting
@@ -105,7 +125,7 @@
     }
 
     private void monoTest(int res) throws Exception {
-        short [] mono = decodeToMemory(res, RESET_MODE_NONE);
+        short [] mono = decodeToMemory(res, RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
         if (mono.length == 44100) {
             // expected
         } else if (mono.length == 88200) {
@@ -118,14 +138,11 @@
             fail("wrong number of samples: " + mono.length);
         }
 
-        // we should get the same data when reconfiguring the codec
-        short [] mono2 = decodeToMemory(res, RESET_MODE_RECONFIGURE);
+        short [] mono2 = decodeToMemory(res, RESET_MODE_RECONFIGURE, CONFIG_MODE_NONE, -1, null);
         assertTrue(Arrays.equals(mono, mono2));
 
-        // NOTE: coming soon
-        // and when flushing it
-//        short [] mono3 = decodeToMemory(res, RESET_MODE_FLUSH);
-//        assertTrue(Arrays.equals(mono, mono3));
+        short [] mono3 = decodeToMemory(res, RESET_MODE_FLUSH, CONFIG_MODE_NONE, -1, null);
+        assertTrue(Arrays.equals(mono, mono3));
     }
 
     /**
@@ -135,7 +152,7 @@
      */
     private void decode(int testinput, float maxerror) throws IOException {
 
-        short [] decoded = decodeToMemory(testinput, RESET_MODE_NONE);
+        short[] decoded = decodeToMemory(testinput, RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
 
         assertEquals("wrong data size", mMasterBuffer.length, decoded.length);
 
@@ -152,22 +169,53 @@
         double rmse = Math.sqrt(avgErrorSquared);
         assertTrue("decoding error too big: " + rmse, rmse <= maxerror);
 
-        short [] decoded2 = decodeToMemory(testinput, RESET_MODE_RECONFIGURE);
-        assertEquals("count different with reconfigure", decoded.length, decoded2.length);
-        for (int i = 0; i < decoded.length; i++) {
-            assertEquals("samples don't match", decoded[i], decoded2[i]);
-        }
+        int[] resetModes = new int[] { RESET_MODE_NONE, RESET_MODE_RECONFIGURE, RESET_MODE_FLUSH };
+        int[] configModes = new int[] { CONFIG_MODE_NONE, CONFIG_MODE_QUEUE };
 
-        // NOTE: coming soon
-//        short [] decoded3 = decodeToMemory(testinput, RESET_MODE_FLUSH);
-//        assertEquals("count different with flush", decoded.length, decoded3.length);
-//        for (int i = 0; i < decoded.length; i++) {
-//            assertEquals("samples don't match", decoded[i], decoded3[i]);
-//        }
+        for (int conf : configModes) {
+            for (int reset : resetModes) {
+                if (conf == CONFIG_MODE_NONE && reset == RESET_MODE_NONE) {
+                    // default case done outside of loop
+                    continue;
+                }
+                if (conf == CONFIG_MODE_QUEUE && !hasAudioCsd(testinput)) {
+                    continue;
+                }
+
+                String params = String.format("(using reset: %d, config: %s)", reset, conf);
+                short[] decoded2 = decodeToMemory(testinput, reset, conf, -1, null);
+                assertEquals("count different with reconfigure" + params,
+                        decoded.length, decoded2.length);
+                for (int i = 0; i < decoded.length; i++) {
+                    assertEquals("samples don't match" + params, decoded[i], decoded2[i]);
+                }
+            }
+        }
     }
 
-    private short[] decodeToMemory(int testinput, int resetMode) throws IOException {
+    private boolean hasAudioCsd(int testinput) throws IOException {
+        AssetFileDescriptor fd = null;
+        try {
 
+            fd = mResources.openRawResourceFd(testinput);
+            MediaExtractor extractor = new MediaExtractor();
+            extractor.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
+            MediaFormat format = extractor.getTrackFormat(0);
+
+            return format.containsKey(CSD_KEYS[0]);
+
+        } finally {
+            if (fd != null) {
+                fd.close();
+            }
+        }
+    }
+
+    private short[] decodeToMemory(int testinput, int resetMode, int configMode,
+            int eossample, List<Long> timestamps) throws IOException {
+
+        String localTag = TAG + "#decodeToMemory";
+        Log.v(localTag, String.format("reset = %d; config: %s", resetMode, configMode));
         short [] decoded = new short[0];
         int decodedIdx = 0;
 
@@ -188,15 +236,32 @@
         String mime = format.getString(MediaFormat.KEY_MIME);
         assertTrue("not an audio file", mime.startsWith("audio/"));
 
+        MediaFormat configFormat = format;
         codec = MediaCodec.createDecoderByType(mime);
-        codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
+        if (configMode == CONFIG_MODE_QUEUE && format.containsKey(CSD_KEYS[0])) {
+            configFormat = MediaFormat.createAudioFormat(mime,
+                    format.getInteger(MediaFormat.KEY_SAMPLE_RATE),
+                    format.getInteger(MediaFormat.KEY_CHANNEL_COUNT));
+
+            configFormat.setLong(MediaFormat.KEY_DURATION,
+                    format.getLong(MediaFormat.KEY_DURATION));
+            String[] keys = new String[] { "max-input-size", "encoder-delay", "encoder-padding" };
+            for (String k : keys) {
+                if (format.containsKey(k)) {
+                    configFormat.setInteger(k, format.getInteger(k));
+                }
+            }
+        }
+        Log.v(localTag, "configuring with " + configFormat);
+        codec.configure(configFormat, null /* surface */, null /* crypto */, 0 /* flags */);
+
         codec.start();
         codecInputBuffers = codec.getInputBuffers();
         codecOutputBuffers = codec.getOutputBuffers();
 
         if (resetMode == RESET_MODE_RECONFIGURE) {
             codec.stop();
-            codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
+            codec.configure(configFormat, null /* surface */, null /* crypto */, 0 /* flags */);
             codec.start();
             codecInputBuffers = codec.getInputBuffers();
             codecOutputBuffers = codec.getOutputBuffers();
@@ -206,12 +271,17 @@
 
         extractor.selectTrack(0);
 
+        if (configMode == CONFIG_MODE_QUEUE) {
+            queueConfig(codec, format);
+        }
+
         // start decoding
         final long kTimeOutUs = 5000;
         MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
         boolean sawInputEOS = false;
         boolean sawOutputEOS = false;
         int noOutputCounter = 0;
+        int samplecounter = 0;
         while (!sawOutputEOS && noOutputCounter < 50) {
             noOutputCounter++;
             if (!sawInputEOS) {
@@ -225,14 +295,20 @@
 
                     long presentationTimeUs = 0;
 
+                    if (sampleSize < 0 && eossample > 0) {
+                        fail("test is broken: never reached eos sample");
+                    }
                     if (sampleSize < 0) {
                         Log.d(TAG, "saw input EOS.");
                         sawInputEOS = true;
                         sampleSize = 0;
                     } else {
+                        if (samplecounter == eossample) {
+                            sawInputEOS = true;
+                        }
+                        samplecounter++;
                         presentationTimeUs = extractor.getSampleTime();
                     }
-
                     codec.queueInputBuffer(
                             inputBufIndex,
                             0 /* offset */,
@@ -253,22 +329,32 @@
 
                 if (info.size > 0) {
                     noOutputCounter = 0;
+                    if (timestamps != null) {
+                        timestamps.add(info.presentationTimeUs);
+                    }
                 }
                 if (info.size > 0 && resetMode != RESET_MODE_NONE) {
                     // once we've gotten some data out of the decoder, reset and start again
                     if (resetMode == RESET_MODE_RECONFIGURE) {
                         codec.stop();
-                        codec.configure(format, null /* surface */, null /* crypto */,
+                        codec.configure(configFormat, null /* surface */, null /* crypto */,
                                 0 /* flags */);
                         codec.start();
                         codecInputBuffers = codec.getInputBuffers();
                         codecOutputBuffers = codec.getOutputBuffers();
+                        if (configMode == CONFIG_MODE_QUEUE) {
+                            queueConfig(codec, format);
+                        }
                     } else /* resetMode == RESET_MODE_FLUSH */ {
                         codec.flush();
                     }
                     resetMode = RESET_MODE_NONE;
                     extractor.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
                     sawInputEOS = false;
+                    samplecounter = 0;
+                    if (timestamps != null) {
+                        timestamps.clear();
+                    }
                     continue;
                 }
 
@@ -279,8 +365,9 @@
                     decoded = Arrays.copyOf(decoded, decodedIdx + (info.size / 2));
                 }
 
+                buf.position(info.offset);
                 for (int i = 0; i < info.size; i += 2) {
-                    decoded[decodedIdx++] = buf.getShort(i);
+                    decoded[decodedIdx++] = buf.getShort();
                 }
 
                 codec.releaseOutputBuffer(outputBufIndex, false /* render */);
@@ -301,12 +388,106 @@
                 Log.d(TAG, "dequeueOutputBuffer returned " + res);
             }
         }
+        if (noOutputCounter >= 50) {
+            fail("decoder stopped outputing data");
+        }
 
         codec.stop();
         codec.release();
         return decoded;
     }
 
+    private void queueConfig(MediaCodec codec, MediaFormat format) {
+        for (String csdKey : CSD_KEYS) {
+            if (!format.containsKey(csdKey)) {
+                continue;
+            }
+            ByteBuffer[] codecInputBuffers = codec.getInputBuffers();
+            int inputBufIndex = codec.dequeueInputBuffer(-1);
+            if (inputBufIndex < 0) {
+                fail("failed to queue configuration buffer " + csdKey);
+            } else {
+                ByteBuffer csd = (ByteBuffer) format.getByteBuffer(csdKey).rewind();
+                Log.v(TAG + "#queueConfig", String.format("queueing %s:%s", csdKey, csd));
+                codecInputBuffers[inputBufIndex].put(csd);
+                codec.queueInputBuffer(
+                        inputBufIndex,
+                        0 /* offset */,
+                        csd.limit(),
+                        0 /* presentation time (us) */,
+                        MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
+            }
+        }
+    }
+
+    public void testDecodeWithEOSOnLastBuffer() throws Exception {
+        testDecodeWithEOSOnLastBuffer(R.raw.sinesweepm4a);
+        testDecodeWithEOSOnLastBuffer(R.raw.sinesweepmp3lame);
+        testDecodeWithEOSOnLastBuffer(R.raw.sinesweepmp3smpb);
+        testDecodeWithEOSOnLastBuffer(R.raw.sinesweepwav);
+        testDecodeWithEOSOnLastBuffer(R.raw.sinesweepflac);
+        testDecodeWithEOSOnLastBuffer(R.raw.sinesweepogg);
+    }
+
+    /* setting EOS on the last full input buffer should be equivalent to setting EOS on an empty
+     * input buffer after all the full ones. */
+    private void testDecodeWithEOSOnLastBuffer(int res) throws Exception {
+        int numsamples = countSamples(res);
+        assertTrue(numsamples != 0);
+
+        List<Long> timestamps1 = new ArrayList<Long>();
+        short[] decode1 = decodeToMemory(res, RESET_MODE_NONE, CONFIG_MODE_NONE, -1, timestamps1);
+
+        List<Long> timestamps2 = new ArrayList<Long>();
+        short[] decode2 = decodeToMemory(res, RESET_MODE_NONE, CONFIG_MODE_NONE, numsamples - 1,
+                timestamps2);
+
+        // check that the data and the timestamps are the same for EOS-on-last and EOS-after-last
+        assertEquals(decode1.length, decode2.length);
+        assertTrue(Arrays.equals(decode1, decode2));
+        assertEquals(timestamps1.size(), timestamps2.size());
+        assertTrue(timestamps1.equals(timestamps2));
+
+        // ... and that this is also true when reconfiguring the codec
+        timestamps2.clear();
+        decode2 = decodeToMemory(res, RESET_MODE_RECONFIGURE, CONFIG_MODE_NONE, -1, timestamps2);
+        assertTrue(Arrays.equals(decode1, decode2));
+        assertTrue(timestamps1.equals(timestamps2));
+        timestamps2.clear();
+        decode2 = decodeToMemory(res, RESET_MODE_RECONFIGURE, CONFIG_MODE_NONE, numsamples - 1,
+                timestamps2);
+        assertEquals(decode1.length, decode2.length);
+        assertTrue(Arrays.equals(decode1, decode2));
+        assertTrue(timestamps1.equals(timestamps2));
+
+        // ... and that this is also true when flushing the codec
+        timestamps2.clear();
+        decode2 = decodeToMemory(res, RESET_MODE_FLUSH, CONFIG_MODE_NONE, -1, timestamps2);
+        assertTrue(Arrays.equals(decode1, decode2));
+        assertTrue(timestamps1.equals(timestamps2));
+        timestamps2.clear();
+        decode2 = decodeToMemory(res, RESET_MODE_FLUSH, CONFIG_MODE_NONE, numsamples - 1,
+                timestamps2);
+        assertEquals(decode1.length, decode2.length);
+        assertTrue(Arrays.equals(decode1, decode2));
+        assertTrue(timestamps1.equals(timestamps2));
+    }
+
+    private int countSamples(int res) throws IOException {
+        AssetFileDescriptor testFd = mResources.openRawResourceFd(res);
+
+        MediaExtractor extractor = new MediaExtractor();
+        extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
+                testFd.getLength());
+        testFd.close();
+        extractor.selectTrack(0);
+        int numsamples = 0;
+        while (extractor.advance()) {
+            numsamples++;
+        }
+        return numsamples;
+    }
+
     public void testCodecBasicH264() throws Exception {
         Surface s = getActivity().getSurfaceHolder().getSurface();
         int frames1 = countFrames(
@@ -472,21 +653,21 @@
 //    }
 
     public void testCodecResetsMp3() throws Exception {
-        testCodecReconfig(R.raw.sinesweepmp3lame, null);
+        testCodecReconfig(R.raw.sinesweepmp3lame);
         // NOTE: replacing testCodecReconfig call soon
 //        testCodecResets(R.raw.sinesweepmp3lame, null);
     }
 
     public void testCodecResetsM4a() throws Exception {
-        testCodecReconfig(R.raw.sinesweepm4a, null);
+        testCodecReconfig(R.raw.sinesweepm4a);
         // NOTE: replacing testCodecReconfig call soon
 //        testCodecResets(R.raw.sinesweepm4a, null);
     }
 
-    private void testCodecReconfig(int video, Surface s) throws Exception {
-        int frames1 = countFrames(video, RESET_MODE_NONE, -1 /* eosframe */, s);
-        int frames2 = countFrames(video, RESET_MODE_RECONFIGURE, -1 /* eosframe */, s);
-        assertEquals("different number of frames when using reconfigured codec", frames1, frames2);
+    private void testCodecReconfig(int audio) throws Exception {
+        int size1 = countSize(audio, RESET_MODE_NONE, -1 /* eosframe */);
+        int size2 = countSize(audio, RESET_MODE_RECONFIGURE, -1 /* eosframe */);
+        assertEquals("different output size when using reconfigured codec", size1, size2);
     }
 
     private void testCodecResets(int video, Surface s) throws Exception {
@@ -497,55 +678,146 @@
         assertEquals("different number of frames when using flushed codec", frames1, frames3);
     }
 
-    private MediaCodec createDecoder(String mime) {
-        if (false) {
-            // change to force testing software codecs
-            if (mime.contains("avc")) {
-                return MediaCodec.createByCodecName("OMX.google.h264.decoder");
-            } else if (mime.contains("3gpp")) {
-                return MediaCodec.createByCodecName("OMX.google.h263.decoder");
-            } else if (mime.contains("mp4v")) {
-                return MediaCodec.createByCodecName("OMX.google.mpeg4.decoder");
-            } else if (mime.contains("vp8")) {
-                return MediaCodec.createByCodecName("OMX.google.vp8.decoder");
-            } else if (mime.contains("vp9")) {
-                return MediaCodec.createByCodecName("OMX.google.vp9.decoder");
+    private static MediaCodec createDecoder(String mime) {
+        try {
+            if (false) {
+                // change to force testing software codecs
+                if (mime.contains("avc")) {
+                    return MediaCodec.createByCodecName("OMX.google.h264.decoder");
+                } else if (mime.contains("3gpp")) {
+                    return MediaCodec.createByCodecName("OMX.google.h263.decoder");
+                } else if (mime.contains("mp4v")) {
+                    return MediaCodec.createByCodecName("OMX.google.mpeg4.decoder");
+                } else if (mime.contains("vp8")) {
+                    return MediaCodec.createByCodecName("OMX.google.vp8.decoder");
+                } else if (mime.contains("vp9")) {
+                    return MediaCodec.createByCodecName("OMX.google.vp9.decoder");
+                }
             }
+            return MediaCodec.createDecoderByType(mime);
+        } catch (Exception e) {
+            return null;
         }
-        return MediaCodec.createDecoderByType(mime);
     }
 
+    // for video
     private int countFrames(int video, int resetMode, int eosframe, Surface s)
             throws Exception {
-        int numframes = 0;
-
         AssetFileDescriptor testFd = mResources.openRawResourceFd(video);
+        MediaExtractor extractor = new MediaExtractor();
+        extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
+                testFd.getLength());
+        extractor.selectTrack(0);
 
-        MediaExtractor extractor;
-        MediaCodec codec = null;
+        int numframes = decodeWithChecks(extractor, CHECKFLAG_RETURN_OUTPUTFRAMES
+                | CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH, resetMode, s,
+                eosframe, null, null);
+
+        extractor.release();
+        testFd.close();
+        return numframes;
+    }
+
+    // for audio
+    private int countSize(int audio, int resetMode, int eosframe)
+            throws Exception {
+        AssetFileDescriptor testFd = mResources.openRawResourceFd(audio);
+        MediaExtractor extractor = new MediaExtractor();
+        extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
+                testFd.getLength());
+        extractor.selectTrack(0);
+
+        // fails CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH
+        int outputSize = decodeWithChecks(extractor, CHECKFLAG_RETURN_OUTPUTSIZE, resetMode, null,
+                eosframe, null, null);
+
+        extractor.release();
+        testFd.close();
+        return outputSize;
+    }
+
+    private void testEOSBehavior(int movie, int stopatsample) throws Exception {
+        testEOSBehavior(movie, new int[] {stopatsample});
+    }
+
+    private void testEOSBehavior(int movie, int[] stopAtSample) throws Exception {
+        Surface s = null;
+        AssetFileDescriptor testFd = mResources.openRawResourceFd(movie);
+        MediaExtractor extractor = new MediaExtractor();
+        extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
+                testFd.getLength());
+        extractor.selectTrack(0); // consider variable looping on track
+        List<Long> outputChecksums = new ArrayList<Long>();
+        List<Long> outputTimestamps = new ArrayList<Long>();
+        Arrays.sort(stopAtSample);
+        int last = stopAtSample.length - 1;
+
+        // decode reference (longest sequence to stop at + 100) and
+        // store checksums/pts in outputChecksums and outputTimestamps
+        // (will fail CHECKFLAG_COMPAREINPUTOUTPUTSAMPLEMATCH)
+        decodeWithChecks(extractor,
+                CHECKFLAG_SETCHECKSUM | CHECKFLAG_SETPTS | CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH,
+                RESET_MODE_NONE, s,
+                stopAtSample[last] + 100, outputChecksums, outputTimestamps);
+
+        // decode stopAtSample requests in reverse order (longest to
+        // shortest) and compare to reference checksums/pts in
+        // outputChecksums and outputTimestamps
+        for (int i = last; i >= 0; --i) {
+            if (true) { // reposition extractor
+                extractor.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
+            } else { // create new extractor
+                extractor.release();
+                extractor = new MediaExtractor();
+                extractor.setDataSource(testFd.getFileDescriptor(),
+                        testFd.getStartOffset(), testFd.getLength());
+                extractor.selectTrack(0); // consider variable looping on track
+            }
+            decodeWithChecks(extractor,
+                    CHECKFLAG_COMPARECHECKSUM | CHECKFLAG_COMPAREPTS
+                    | CHECKFLAG_COMPAREINPUTOUTPUTSAMPLEMATCH
+                    | CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH,
+                    RESET_MODE_NONE, s,
+                    stopAtSample[i], outputChecksums, outputTimestamps);
+        }
+        extractor.release();
+        testFd.close();
+    }
+
+    private static final int CHECKFLAG_SETCHECKSUM = 1 << 0;
+    private static final int CHECKFLAG_COMPARECHECKSUM = 1 << 1;
+    private static final int CHECKFLAG_SETPTS = 1 << 2;
+    private static final int CHECKFLAG_COMPAREPTS = 1 << 3;
+    private static final int CHECKFLAG_COMPAREINPUTOUTPUTSAMPLEMATCH = 1 << 4;
+    private static final int CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH = 1 << 5;
+    private static final int CHECKFLAG_RETURN_OUTPUTFRAMES = 1 << 6;
+    private static final int CHECKFLAG_RETURN_OUTPUTSIZE = 1 << 7;
+
+    /**
+     * Decodes frames with parameterized checks and return values.
+     * The integer return can be selected through the checkFlags variable.
+     */
+    private static int decodeWithChecks(MediaExtractor extractor, int checkFlags, int resetMode,
+            Surface surface, int stopAtSample,
+            List<Long> outputChecksums, List<Long> outputTimestamps)
+            throws Exception {
+        int trackIndex = extractor.getSampleTrackIndex();
+        MediaFormat format = extractor.getTrackFormat(trackIndex);
+        String mime = format.getString(MediaFormat.KEY_MIME);
+        boolean isAudio = mime.startsWith("audio/");
         ByteBuffer[] codecInputBuffers;
         ByteBuffer[] codecOutputBuffers;
 
-        extractor = new MediaExtractor();
-        extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
-                testFd.getLength());
-
-        MediaFormat format = extractor.getTrackFormat(0);
-        String mime = format.getString(MediaFormat.KEY_MIME);
-        boolean isAudio = mime.startsWith("audio/");
-
-        codec = createDecoder(mime);
-
-        assertNotNull("couldn't find codec", codec);
+        MediaCodec codec = createDecoder(mime);
         Log.i("@@@@", "using codec: " + codec.getName());
-        codec.configure(format, s /* surface */, null /* crypto */, 0 /* flags */);
+        codec.configure(format, surface, null /* crypto */, 0 /* flags */);
         codec.start();
         codecInputBuffers = codec.getInputBuffers();
         codecOutputBuffers = codec.getOutputBuffers();
 
         if (resetMode == RESET_MODE_RECONFIGURE) {
             codec.stop();
-            codec.configure(format, s /* surface */, null /* crypto */, 0 /* flags */);
+            codec.configure(format, surface, null /* crypto */, 0 /* flags */);
             codec.start();
             codecInputBuffers = codec.getInputBuffers();
             codecOutputBuffers = codec.getOutputBuffers();
@@ -553,19 +825,28 @@
             codec.flush();
         }
 
-        Log.i("@@@@", "format: " + format);
-
-        extractor.selectTrack(0);
-
-        // start decoding
-        final long kTimeOutUs = 5000;
+        // start decode loop
         MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+
+        final long kTimeOutUs = 5000; // 5ms timeout
         boolean sawInputEOS = false;
         boolean sawOutputEOS = false;
         int deadDecoderCounter = 0;
-        int samplecounter = 0;
+        int samplenum = 0;
+        int numframes = 0;
+        int outputSize = 0;
+        int width = 0;
+        int height = 0;
+        boolean dochecksum = false;
         ArrayList<Long> timestamps = new ArrayList<Long>();
+        if ((checkFlags & CHECKFLAG_SETPTS) != 0) {
+            outputTimestamps.clear();
+        }
+        if ((checkFlags & CHECKFLAG_SETCHECKSUM) != 0) {
+            outputChecksums.clear();
+        }
         while (!sawOutputEOS && deadDecoderCounter < 100) {
+            // handle input
             if (!sawInputEOS) {
                 int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs);
 
@@ -573,418 +854,286 @@
                     ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
 
                     int sampleSize =
-                        extractor.readSampleData(dstBuf, 0 /* offset */);
-
-                    long presentationTimeUs = 0;
+                            extractor.readSampleData(dstBuf, 0 /* offset */);
+                    long presentationTimeUs = extractor.getSampleTime();
+                    boolean advanceDone = extractor.advance();
+                    // int flags = extractor.getSampleFlags();
+                    // Log.i("@@@@", "read sample " + samplenum + ":" +
+                    // extractor.getSampleFlags()
+                    // + " @ " + extractor.getSampleTime() + " size " +
+                    // sampleSize);
+                    assertEquals("extractor.advance() should match end of stream", sampleSize >= 0,
+                            advanceDone);
 
                     if (sampleSize < 0) {
                         Log.d(TAG, "saw input EOS.");
                         sawInputEOS = true;
-                        sampleSize = 0;
+                        assertEquals("extractor.readSampleData() must return -1 at end of stream",
+                                -1, sampleSize);
+                        assertEquals("extractor.getSampleTime() must return -1 at end of stream",
+                                -1, presentationTimeUs);
+                        sampleSize = 0; // required otherwise queueInputBuffer
+                                        // returns invalid.
                     } else {
-                        presentationTimeUs = extractor.getSampleTime();
-                        samplecounter++;
-                        if (samplecounter == eosframe) {
-                            sawInputEOS = true;
+                        timestamps.add(presentationTimeUs);
+                        samplenum++; // increment before comparing with stopAtSample
+                        if (samplenum == stopAtSample) {
+                            Log.d(TAG, "saw input EOS (stop at sample).");
+                            sawInputEOS = true; // tag this sample as EOS
                         }
                     }
-
-                    timestamps.add(presentationTimeUs);
-
-                    int flags = extractor.getSampleFlags();
-
                     codec.queueInputBuffer(
                             inputBufIndex,
                             0 /* offset */,
                             sampleSize,
                             presentationTimeUs,
                             sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
-
-                    if (!sawInputEOS) {
-                        extractor.advance();
-                    }
+                } else {
+                    assertEquals(
+                            "codec.dequeueInputBuffer() unrecognized return value: " + inputBufIndex,
+                            MediaCodec.INFO_TRY_AGAIN_LATER, inputBufIndex);
                 }
             }
 
-            int res = codec.dequeueOutputBuffer(info, kTimeOutUs);
+            // handle output
+            int outputBufIndex = codec.dequeueOutputBuffer(info, kTimeOutUs);
 
             deadDecoderCounter++;
-            if (res >= 0) {
-                //Log.d(TAG, "got frame, size " + info.size + "/" + info.presentationTimeUs);
-
-                // Some decoders output a 0-sized buffer at the end. Disregard those.
-                if (info.size > 0) {
+            if (outputBufIndex >= 0) {
+                if (info.size > 0) { // Disregard 0-sized buffers at the end.
                     deadDecoderCounter = 0;
                     if (resetMode != RESET_MODE_NONE) {
-                        // once we've gotten some data out of the decoder, reset and start again
+                        // once we've gotten some data out of the decoder, reset
+                        // and start again
                         if (resetMode == RESET_MODE_RECONFIGURE) {
                             codec.stop();
-                            codec.configure(format, s /* surface */, null /* crypto */,
+                            codec.configure(format, surface /* surface */, null /* crypto */,
                                     0 /* flags */);
                             codec.start();
                             codecInputBuffers = codec.getInputBuffers();
                             codecOutputBuffers = codec.getOutputBuffers();
-                        } else /* resetMode == RESET_MODE_FLUSH */ {
+                        } else if (resetMode == RESET_MODE_FLUSH) {
                             codec.flush();
+                        } else {
+                            fail("unknown resetMode: " + resetMode);
                         }
+                        // restart at beginning, clear resetMode
                         resetMode = RESET_MODE_NONE;
                         extractor.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
                         sawInputEOS = false;
                         numframes = 0;
                         timestamps.clear();
+                        if ((checkFlags & CHECKFLAG_SETPTS) != 0) {
+                            outputTimestamps.clear();
+                        }
+                        if ((checkFlags & CHECKFLAG_SETCHECKSUM) != 0) {
+                            outputChecksums.clear();
+                        }
                         continue;
                     }
-
-                    if (isAudio) {
-                        // for audio, count the number of bytes that were decoded, not the number
-                        // of access units
-                        numframes += info.size;
-                    } else {
-                        // for video, count the number of video frames and check the timestamp
-                        numframes++;
-                        assertTrue("invalid timestamp", timestamps.remove(info.presentationTimeUs));
+                    if ((checkFlags & CHECKFLAG_COMPAREPTS) != 0) {
+                        assertTrue("number of frames (" + numframes
+                                + ") exceeds number of reference timestamps",
+                                numframes < outputTimestamps.size());
+                        assertEquals("frame ts mismatch at frame " + numframes,
+                                (long) outputTimestamps.get(numframes), info.presentationTimeUs);
+                    } else if ((checkFlags & CHECKFLAG_SETPTS) != 0) {
+                        outputTimestamps.add(info.presentationTimeUs);
                     }
+                    if ((checkFlags & (CHECKFLAG_SETCHECKSUM | CHECKFLAG_COMPARECHECKSUM)) != 0) {
+                        long sum = 0;   // note: checksum is 0 if buffer format unrecognized
+                        if (dochecksum) {
+                            // TODO: add stride - right now just use info.size (as before)
+                            //sum = checksum(codecOutputBuffers[outputBufIndex], width, height,
+                            //        stride);
+                            ByteBuffer outputBuffer = codecOutputBuffers[outputBufIndex];
+                            outputBuffer.position(info.offset);
+                            sum = checksum(outputBuffer, info.size);
+                        }
+                        if ((checkFlags & CHECKFLAG_COMPARECHECKSUM) != 0) {
+                            assertTrue("number of frames (" + numframes
+                                    + ") exceeds number of reference checksums",
+                                    numframes < outputChecksums.size());
+                            Log.d(TAG, "orig checksum: " + outputChecksums.get(numframes)
+                                    + " new checksum: " + sum);
+                            assertEquals("frame data mismatch at frame " + numframes,
+                                    (long) outputChecksums.get(numframes), sum);
+                        } else if ((checkFlags & CHECKFLAG_SETCHECKSUM) != 0) {
+                            outputChecksums.add(sum);
+                        }
+                    }
+                    if ((checkFlags & CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH) != 0) {
+                        assertTrue("output timestamp " + info.presentationTimeUs
+                                + " without corresponding input timestamp"
+                                , timestamps.remove(info.presentationTimeUs));
+                    }
+                    outputSize += info.size;
+                    numframes++;
                 }
-                int outputBufIndex = res;
+                // Log.d(TAG, "got frame, size " + info.size + "/" +
+                // info.presentationTimeUs +
+                // "/" + numframes + "/" + info.flags);
                 codec.releaseOutputBuffer(outputBufIndex, true /* render */);
-
                 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                     Log.d(TAG, "saw output EOS.");
                     sawOutputEOS = true;
                 }
-            } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+            } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                 codecOutputBuffers = codec.getOutputBuffers();
-
                 Log.d(TAG, "output buffers have changed.");
-            } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+            } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                 MediaFormat oformat = codec.getOutputFormat();
-
-                Log.d(TAG, "output format has changed to " + oformat);
+                if (oformat.containsKey(MediaFormat.KEY_COLOR_FORMAT) &&
+                        oformat.containsKey(MediaFormat.KEY_WIDTH) &&
+                        oformat.containsKey(MediaFormat.KEY_HEIGHT)) {
+                    int colorFormat = oformat.getInteger(MediaFormat.KEY_COLOR_FORMAT);
+                    width = oformat.getInteger(MediaFormat.KEY_WIDTH);
+                    height = oformat.getInteger(MediaFormat.KEY_HEIGHT);
+                    dochecksum = isRecognizedFormat(colorFormat); // only checksum known raw
+                                                                  // buf formats
+                    Log.d(TAG, "checksum fmt: " + colorFormat + " dim " + width + "x" + height);
+                } else {
+                    dochecksum = false; // check with audio later
+                    width = height = 0;
+                    Log.d(TAG, "output format has changed to (unknown video) " + oformat);
+                }
             } else {
-                Log.d(TAG, "no output");
+                assertEquals(
+                        "codec.dequeueOutputBuffer() unrecognized return index: "
+                                + outputBufIndex,
+                        MediaCodec.INFO_TRY_AGAIN_LATER, outputBufIndex);
             }
         }
-
         codec.stop();
         codec.release();
-        testFd.close();
-        return numframes;
+
+        assertTrue("last frame didn't have EOS", sawOutputEOS);
+        if ((checkFlags & CHECKFLAG_COMPAREINPUTOUTPUTSAMPLEMATCH) != 0) {
+            assertEquals("I!=O", samplenum, numframes);
+            if (stopAtSample != 0) {
+                assertEquals("did not stop with right number of frames", stopAtSample, numframes);
+            }
+        }
+        return (checkFlags & CHECKFLAG_RETURN_OUTPUTSIZE) != 0 ? outputSize :
+                (checkFlags & CHECKFLAG_RETURN_OUTPUTFRAMES) != 0 ? numframes :
+                        0;
     }
 
     public void testEOSBehaviorH264() throws Exception {
         // this video has an I frame at 44
-        testEOSBehavior(R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, 44);
-        testEOSBehavior(R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, 45);
-        testEOSBehavior(R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, 55);
+        testEOSBehavior(R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz,
+                new int[] {44, 45, 55});
     }
 
     public void testEOSBehaviorH263() throws Exception {
         // this video has an I frame every 12 frames.
-        testEOSBehavior(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 24);
-        testEOSBehavior(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 25);
-        testEOSBehavior(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 48);
-        testEOSBehavior(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 50);
+        testEOSBehavior(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz,
+                new int[] {24, 25, 48, 50});
     }
 
     public void testEOSBehaviorMpeg4() throws Exception {
         // this video has an I frame every 12 frames
-        testEOSBehavior(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, 24);
-        testEOSBehavior(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, 25);
-        testEOSBehavior(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, 48);
-        testEOSBehavior(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, 50);
-        testEOSBehavior(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, 2);
+        testEOSBehavior(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz,
+                new int[] {24, 25, 48, 50, 2});
     }
 
     public void testEOSBehaviorVP8() throws Exception {
         // this video has an I frame at 46
-        testEOSBehavior(R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 46);
-        testEOSBehavior(R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 47);
-        testEOSBehavior(R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 57);
-        testEOSBehavior(R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 45);
+        testEOSBehavior(R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz,
+                new int[] {46, 47, 57, 45});
     }
 
     public void testEOSBehaviorVP9() throws Exception {
         // this video has an I frame at 44
-        testEOSBehavior(R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 44);
-        testEOSBehavior(R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 45);
-        testEOSBehavior(R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 55);
-        testEOSBehavior(R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 43);
-    }
-
-    private void testEOSBehavior(int movie, int stopatsample) throws Exception {
-
-        int numframes = 0;
-
-        long [] checksums = new long[stopatsample];
-
-        AssetFileDescriptor testFd = mResources.openRawResourceFd(movie);
-
-        MediaExtractor extractor;
-        MediaCodec codec = null;
-        ByteBuffer[] codecInputBuffers;
-        ByteBuffer[] codecOutputBuffers;
-
-        extractor = new MediaExtractor();
-        extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
-                testFd.getLength());
-
-        MediaFormat format = extractor.getTrackFormat(0);
-        String mime = format.getString(MediaFormat.KEY_MIME);
-        boolean isAudio = mime.startsWith("audio/");
-
-        codec = createDecoder(mime);
-
-        assertNotNull("couldn't find codec", codec);
-        Log.i("@@@@", "using codec: " + codec.getName());
-        codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
-        codec.start();
-        codecInputBuffers = codec.getInputBuffers();
-        codecOutputBuffers = codec.getOutputBuffers();
-
-        extractor.selectTrack(0);
-
-        // start decoding
-        final long kTimeOutUs = 5000;
-        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
-        boolean sawInputEOS = false;
-        boolean sawOutputEOS = false;
-        int deadDecoderCounter = 0;
-        int samplenum = 0;
-        boolean dochecksum = false;
-        while (!sawOutputEOS && deadDecoderCounter < 100) {
-            if (!sawInputEOS) {
-                int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs);
-
-                if (inputBufIndex >= 0) {
-                    ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
-
-                    int sampleSize =
-                        extractor.readSampleData(dstBuf, 0 /* offset */);
-//                    Log.i("@@@@", "read sample " + samplenum + ":" + extractor.getSampleFlags()
-//                            + " @ " + extractor.getSampleTime() + " size " + sampleSize);
-                    samplenum++;
-
-                    long presentationTimeUs = 0;
-
-                    if (sampleSize < 0 || samplenum >= (stopatsample + 100)) {
-                        Log.d(TAG, "saw input EOS.");
-                        sawInputEOS = true;
-                        sampleSize = 0;
-                    } else {
-                        presentationTimeUs = extractor.getSampleTime();
-                    }
-
-                    int flags = extractor.getSampleFlags();
-
-                    codec.queueInputBuffer(
-                            inputBufIndex,
-                            0 /* offset */,
-                            sampleSize,
-                            presentationTimeUs,
-                            sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
-
-                    if (!sawInputEOS) {
-                        extractor.advance();
-                    }
-                }
-            }
-
-            int res = codec.dequeueOutputBuffer(info, kTimeOutUs);
-
-            deadDecoderCounter++;
-            if (res >= 0) {
-
-                // Some decoders output a 0-sized buffer at the end. Disregard those.
-                if (info.size > 0) {
-                    deadDecoderCounter = 0;
-
-                    if (isAudio) {
-                        // for audio, count the number of bytes that were decoded, not the number
-                        // of access units
-                        numframes += info.size;
-                    } else {
-                        // for video, count the number of video frames
-                        long sum = dochecksum ? checksum(codecOutputBuffers[res], info.size) : 0;
-                        if (numframes < checksums.length) {
-                            checksums[numframes] = sum;
-                        }
-                        numframes++;
-                    }
-                }
-//                Log.d(TAG, "got frame, size " + info.size + "/" + info.presentationTimeUs +
-//                        "/" + numframes + "/" + info.flags);
-
-                int outputBufIndex = res;
-                codec.releaseOutputBuffer(outputBufIndex, true /* render */);
-
-                if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
-                    Log.d(TAG, "saw output EOS.");
-                    sawOutputEOS = true;
-                }
-            } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
-                codecOutputBuffers = codec.getOutputBuffers();
-
-                Log.d(TAG, "output buffers have changed.");
-            } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
-                MediaFormat oformat = codec.getOutputFormat();
-                int colorFormat = oformat.getInteger(MediaFormat.KEY_COLOR_FORMAT);
-                dochecksum = isRecognizedFormat(colorFormat);
-                Log.d(TAG, "output format has changed to " + oformat);
-            } else {
-                Log.d(TAG, "no output");
-            }
-        }
-
-        codec.stop();
-        codec.release();
-        extractor.release();
-
-
-        // We now have checksums for every frame.
-        // Now decode again, but signal EOS right before an index frame, and ensure the frames
-        // prior to that are the same.
-
-        extractor = new MediaExtractor();
-        extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
-                testFd.getLength());
-
-        codec = createDecoder(mime);
-        codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
-        codec.start();
-        codecInputBuffers = codec.getInputBuffers();
-        codecOutputBuffers = codec.getOutputBuffers();
-
-        extractor.selectTrack(0);
-
-        // start decoding
-        info = new MediaCodec.BufferInfo();
-        sawInputEOS = false;
-        sawOutputEOS = false;
-        deadDecoderCounter = 0;
-        samplenum = 0;
-        numframes = 0;
-        dochecksum = false;
-        while (!sawOutputEOS && deadDecoderCounter < 100) {
-            if (!sawInputEOS) {
-                int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs);
-
-                if (inputBufIndex >= 0) {
-                    ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
-
-                    int sampleSize =
-                        extractor.readSampleData(dstBuf, 0 /* offset */);
-//                    Log.i("@@@@", "read sample " + samplenum + ":" + extractor.getSampleFlags()
-//                            + " @ " + extractor.getSampleTime() + " size " + sampleSize);
-                    samplenum++;
-
-                    long presentationTimeUs = extractor.getSampleTime();
-
-                    if (sampleSize < 0 || samplenum >= stopatsample) {
-                        Log.d(TAG, "saw input EOS.");
-                        sawInputEOS = true;
-                        if (sampleSize < 0) {
-                            sampleSize = 0;
-                        }
-                    }
-
-                    int flags = extractor.getSampleFlags();
-
-                    codec.queueInputBuffer(
-                            inputBufIndex,
-                            0 /* offset */,
-                            sampleSize,
-                            presentationTimeUs,
-                            sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
-
-                    if (!sawInputEOS) {
-                        extractor.advance();
-                    }
-                }
-            }
-
-            int res = codec.dequeueOutputBuffer(info, kTimeOutUs);
-
-            deadDecoderCounter++;
-            if (res >= 0) {
-
-                // Some decoders output a 0-sized buffer at the end. Disregard those.
-                if (info.size > 0) {
-                    deadDecoderCounter = 0;
-
-                    if (isAudio) {
-                        // for audio, count the number of bytes that were decoded, not the number
-                        // of access units
-                        numframes += info.size;
-                    } else {
-                        // for video, count the number of video frames
-                        long sum = dochecksum ? checksum(codecOutputBuffers[res], info.size) : 0;
-                        if (numframes < checksums.length) {
-                            assertEquals("frame data mismatch at frame " + numframes,
-                                    checksums[numframes], sum);
-                        }
-                        numframes++;
-                    }
-                }
-//                Log.d(TAG, "got frame, size " + info.size + "/" + info.presentationTimeUs +
-//                        "/" + numframes + "/" + info.flags);
-
-                int outputBufIndex = res;
-                codec.releaseOutputBuffer(outputBufIndex, true /* render */);
-
-                if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
-                    Log.d(TAG, "saw output EOS.");
-                    sawOutputEOS = true;
-                }
-            } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
-                codecOutputBuffers = codec.getOutputBuffers();
-
-                Log.d(TAG, "output buffers have changed.");
-            } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
-                MediaFormat oformat = codec.getOutputFormat();
-                int colorFormat = oformat.getInteger(MediaFormat.KEY_COLOR_FORMAT);
-                dochecksum = isRecognizedFormat(colorFormat);
-                Log.d(TAG, "output format has changed to " + oformat);
-            } else {
-                Log.d(TAG, "no output");
-            }
-        }
-
-        codec.stop();
-        codec.release();
-        extractor.release();
-
-        assertEquals("I!=O", samplenum, numframes);
-        assertTrue("last frame didn't have EOS", sawOutputEOS);
-        assertEquals(stopatsample, numframes);
-
-        testFd.close();
+        testEOSBehavior(R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz,
+                new int[] {44, 45, 55, 43});
     }
 
     /* from EncodeDecodeTest */
     private static boolean isRecognizedFormat(int colorFormat) {
+        // Log.d(TAG, "color format: " + String.format("0x%08x", colorFormat));
         switch (colorFormat) {
-            // these are the formats we know how to handle for this test
+        // these are the formats we know how to handle for this test
             case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
             case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
             case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
             case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:
             case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:
+            case MediaCodecInfo.CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar:
+                /*
+                 * TODO: Check newer formats or ignore.
+                 * OMX_SEC_COLOR_FormatNV12Tiled = 0x7FC00002
+                 * OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka = 0x7FA30C03: N4/N7_2
+                 * OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar32m = 0x7FA30C04: N5
+                 */
                 return true;
             default:
                 return false;
         }
     }
 
-    private long checksum(ByteBuffer buf, int size) {
-        assertTrue(size != 0);
-        assertTrue(size <= buf.capacity());
+    private static long checksum(ByteBuffer buf, int size) {
+        int cap = buf.capacity();
+        assertTrue("checksum() params are invalid: size = " + size + " cap = " + cap,
+                size > 0 && size <= cap);
         CRC32 crc = new CRC32();
-        int pos = buf.position();
-        buf.rewind();
-        for (int i = 0; i < size; i++) {
-            crc.update(buf.get());
+        if (buf.hasArray()) {
+            crc.update(buf.array(), buf.position() + buf.arrayOffset(), size);
+        } else {
+            int pos = buf.position();
+            final int rdsize = Math.min(4096, size);
+            byte bb[] = new byte[rdsize];
+            int chk;
+            for (int i = 0; i < size; i += chk) {
+                chk = Math.min(rdsize, size - i);
+                buf.get(bb, 0, chk);
+                crc.update(bb, 0, chk);
+            }
+            buf.position(pos);
         }
-        buf.position(pos);
+        return crc.getValue();
+    }
+
+    private static long checksum(ByteBuffer buf, int width, int height, int stride) {
+        int cap = buf.capacity();
+        assertTrue("checksum() params are invalid: w x h , s = "
+                + width + " x " + height + " , " + stride + " cap = " + cap,
+                width > 0 && width <= stride && height > 0 && height * stride <= cap);
+        // YUV 4:2:0 should generally have a data storage height 1.5x greater
+        // than the declared image height, representing the UV planes.
+        //
+        // We only check Y frame for now. Somewhat unknown with tiling effects.
+        //
+        //long tm = System.nanoTime();
+        final int lineinterval = 1; // line sampling frequency
+        CRC32 crc = new CRC32();
+        if (buf.hasArray()) {
+            byte b[] = buf.array();
+            int offs = buf.arrayOffset();
+            for (int i = 0; i < height; i += lineinterval) {
+                crc.update(b, i * stride + offs, width);
+            }
+        } else { // almost always ends up here due to direct buffers
+            int pos = buf.position();
+            if (true) { // this {} is 80x times faster than else {} below.
+                byte[] bb = new byte[width]; // local line buffer
+                for (int i = 0; i < height; i += lineinterval) {
+                    buf.position(i * stride);
+                    buf.get(bb, 0, width);
+                    crc.update(bb, 0, width);
+                }
+            } else {
+                for (int i = 0; i < height; i += lineinterval) {
+                    buf.position(i * stride);
+                    for (int j = 0; j < width; ++j) {
+                        crc.update(buf.get());
+                    }
+                }
+            }
+            buf.position(pos);
+        }
+        //tm = System.nanoTime() - tm;
+        //Log.d(TAG, "checksum time " + tm);
         return crc.getValue();
     }
 
@@ -1017,6 +1166,8 @@
         assertTrue("not an audio file", mime.startsWith("audio/"));
 
         codec = MediaCodec.createDecoderByType(mime);
+        assertNotNull("couldn't find codec " + mime, codec);
+
         codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
         codec.start();
         codecInputBuffers = codec.getInputBuffers();
@@ -1073,8 +1224,9 @@
                 int outputBufIndex = res;
                 ByteBuffer buf = codecOutputBuffers[outputBufIndex];
 
+                buf.position(info.offset);
                 for (int i = 0; i < info.size; i += 2) {
-                    short sample = buf.getShort(i);
+                    short sample = buf.getShort();
                     if (maxvalue < sample) {
                         maxvalue = sample;
                     }
diff --git a/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java b/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java
index 6f1faf6..9e42414 100644
--- a/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java
+++ b/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java
@@ -1000,16 +1000,17 @@
             x += cropLeft;
 
             int testY, testU, testV;
+            int off = frameData.position();
             if (semiPlanar) {
                 // Galaxy Nexus uses OMX_TI_COLOR_FormatYUV420PackedSemiPlanar
-                testY = frameData.get(y * width + x) & 0xff;
-                testU = frameData.get(width*height + 2*(y/2) * halfWidth + 2*(x/2)) & 0xff;
-                testV = frameData.get(width*height + 2*(y/2) * halfWidth + 2*(x/2) + 1) & 0xff;
+                testY = frameData.get(off + y * width + x) & 0xff;
+                testU = frameData.get(off + width*height + 2*(y/2) * halfWidth + 2*(x/2)) & 0xff;
+                testV = frameData.get(off + width*height + 2*(y/2) * halfWidth + 2*(x/2) + 1) & 0xff;
             } else {
                 // Nexus 10, Nexus 7 use COLOR_FormatYUV420Planar
-                testY = frameData.get(y * width + x) & 0xff;
-                testU = frameData.get(width*height + (y/2) * halfWidth + (x/2)) & 0xff;
-                testV = frameData.get(width*height + halfWidth * (height / 2) +
+                testY = frameData.get(off + y * width + x) & 0xff;
+                testU = frameData.get(off + width*height + (y/2) * halfWidth + (x/2)) & 0xff;
+                testV = frameData.get(off + width*height + halfWidth * (height / 2) +
                         (y/2) * halfWidth + (x/2)) & 0xff;
             }
 
diff --git a/tests/tests/media/src/android/media/cts/EncodeVirtualDisplayTest.java b/tests/tests/media/src/android/media/cts/EncodeVirtualDisplayTest.java
index 9f76ddf..eb06eda 100644
--- a/tests/tests/media/src/android/media/cts/EncodeVirtualDisplayTest.java
+++ b/tests/tests/media/src/android/media/cts/EncodeVirtualDisplayTest.java
@@ -153,7 +153,7 @@
     /**
      * Prepares the encoder, decoder, and virtual display.
      */
-    private void encodeVirtualDisplayTest() {
+    private void encodeVirtualDisplayTest() throws IOException {
         MediaCodec encoder = null;
         MediaCodec decoder = null;
         OutputSurface outputSurface = null;
diff --git a/tests/tests/media/src/android/media/cts/EncoderTest.java b/tests/tests/media/src/android/media/cts/EncoderTest.java
index e9d0b5f..c2e59d4 100644
--- a/tests/tests/media/src/android/media/cts/EncoderTest.java
+++ b/tests/tests/media/src/android/media/cts/EncoderTest.java
@@ -187,8 +187,13 @@
     }
 
     private void testEncoder(String componentName, MediaFormat format) {
-        MediaCodec codec = MediaCodec.createByCodecName(componentName);
-
+        MediaCodec codec;
+        try {
+            codec = MediaCodec.createByCodecName(componentName);
+        } catch (Exception e) {
+            fail("codec '" + componentName + "' failed construction.");
+            return; /* does not get here, but avoids warning */
+        }
         try {
             codec.configure(
                     format,
@@ -196,9 +201,7 @@
                     null /* crypto */,
                     MediaCodec.CONFIGURE_FLAG_ENCODE);
         } catch (IllegalStateException e) {
-            Log.e(TAG, "codec '" + componentName + "' failed configuration.");
-
-            assertTrue("codec '" + componentName + "' failed configuration.", false);
+            fail("codec '" + componentName + "' failed configuration.");
         }
 
         codec.start();
diff --git a/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java b/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
index 81296e4..264a76a 100644
--- a/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
+++ b/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
@@ -469,7 +469,8 @@
      * @param inputFormat the format of the stream to decode
      * @param surface into which to decode the frames
      */
-    private MediaCodec createVideoDecoder(MediaFormat inputFormat, Surface surface) {
+    private MediaCodec createVideoDecoder(MediaFormat inputFormat, Surface surface)
+            throws IOException {
         MediaCodec decoder = MediaCodec.createDecoderByType(getMimeTypeFor(inputFormat));
         decoder.configure(inputFormat, surface, null, 0);
         decoder.start();
@@ -489,7 +490,8 @@
     private MediaCodec createVideoEncoder(
             MediaCodecInfo codecInfo,
             MediaFormat format,
-            AtomicReference<Surface> surfaceReference) {
+            AtomicReference<Surface> surfaceReference)
+            throws IOException {
         MediaCodec encoder = MediaCodec.createByCodecName(codecInfo.getName());
         encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
         // Must be called before start() is.
@@ -503,7 +505,8 @@
      *
      * @param inputFormat the format of the stream to decode
      */
-    private MediaCodec createAudioDecoder(MediaFormat inputFormat) {
+    private MediaCodec createAudioDecoder(MediaFormat inputFormat)
+            throws IOException {
         MediaCodec decoder = MediaCodec.createDecoderByType(getMimeTypeFor(inputFormat));
         decoder.configure(inputFormat, null, null, 0);
         decoder.start();
@@ -516,7 +519,8 @@
      * @param codecInfo of the codec to use
      * @param format of the stream to be produced
      */
-    private MediaCodec createAudioEncoder(MediaCodecInfo codecInfo, MediaFormat format) {
+    private MediaCodec createAudioEncoder(MediaCodecInfo codecInfo, MediaFormat format) 
+            throws IOException {
         MediaCodec encoder = MediaCodec.createByCodecName(codecInfo.getName());
         encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
         encoder.start();
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecListTest.java b/tests/tests/media/src/android/media/cts/MediaCodecListTest.java
index 3428e86..9c07cf1 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecListTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecListTest.java
@@ -26,6 +26,7 @@
 import android.util.Log;
 
 import java.io.File;
+import java.io.IOException;
 import java.util.List;
 import java.util.ArrayList;
 
@@ -56,7 +57,7 @@
 
     // Each component advertised by MediaCodecList should at least be
     // instantiate-able.
-    public void testComponentInstantiation() {
+    public void testComponentInstantiation() throws IOException {
         Log.d(TAG, "testComponentInstantiation");
 
         int codecCount = MediaCodecList.getCodecCount();
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecTest.java b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
index 4b8abcd..ae14e6f 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
@@ -87,7 +87,11 @@
         format.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);
 
         try {
-            encoder = MediaCodec.createByCodecName(codecInfo.getName());
+            try {
+                encoder = MediaCodec.createByCodecName(codecInfo.getName());
+            } catch (IOException e) {
+                fail("failed to create codec " + codecInfo.getName());
+            }
             try {
                 surface = encoder.createInputSurface();
                 fail("createInputSurface should not work pre-configure");
@@ -120,7 +124,6 @@
         assertNull(surface);
     }
 
-
     /**
      * Tests:
      * <br> signaling end-of-stream before any data is sent works
@@ -133,7 +136,11 @@
         InputSurface inputSurface = null;
 
         try {
-            encoder = MediaCodec.createEncoderByType(MIME_TYPE);
+            try {
+                encoder = MediaCodec.createEncoderByType(MIME_TYPE);
+            } catch (IOException e) {
+                fail("failed to create " + MIME_TYPE + " encoder");
+            }
             encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
             inputSurface = new InputSurface(encoder.createInputSurface());
             inputSurface.makeCurrent();
@@ -173,6 +180,61 @@
 
     /**
      * Tests:
+     * <br> stopping with buffers in flight doesn't crash or hang
+     */
+    public void testAbruptStop() {
+        // There appears to be a race, so run it several times with a short delay between runs
+        // to allow any previous activity to shut down.
+        for (int i = 0; i < 50; i++) {
+            Log.d(TAG, "testAbruptStop " + i);
+            doTestAbruptStop();
+            try { Thread.sleep(400); } catch (InterruptedException ignored) {}
+        }
+    }
+    private void doTestAbruptStop() {
+        MediaFormat format = createMediaFormat();
+        MediaCodec encoder = null;
+        InputSurface inputSurface = null;
+
+        try {
+            try {
+                encoder = MediaCodec.createEncoderByType(MIME_TYPE);
+            } catch (IOException e) {
+                fail("failed to create " + MIME_TYPE + " encoder");
+            }
+            encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
+            inputSurface = new InputSurface(encoder.createInputSurface());
+            inputSurface.makeCurrent();
+            encoder.start();
+
+            int totalBuffers = encoder.getInputBuffers().length +
+                    encoder.getOutputBuffers().length;
+            if (VERBOSE) Log.d(TAG, "Total buffers: " + totalBuffers);
+
+            // Submit several frames quickly, without draining the encoder output, to try to
+            // ensure that we've got some queued up when we call stop().  If we do too many
+            // we'll block in swapBuffers().
+            for (int i = 0; i < totalBuffers; i++) {
+                GLES20.glClearColor(0.0f, (i % 8) / 8.0f, 0.0f, 1.0f);
+                GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+                inputSurface.swapBuffers();
+            }
+            Log.d(TAG, "stopping");
+            encoder.stop();
+            Log.d(TAG, "stopped");
+        } finally {
+            if (encoder != null) {
+                encoder.stop();
+                encoder.release();
+            }
+            if (inputSurface != null) {
+                inputSurface.release();
+            }
+        }
+    }
+
+    /**
+     * Tests:
      * <br> dequeueInputBuffer() fails when encoder configured with an input Surface
      */
     public void testDequeueSurface() {
@@ -181,7 +243,11 @@
         Surface surface = null;
 
         try {
-            encoder = MediaCodec.createEncoderByType(MIME_TYPE);
+            try {
+                encoder = MediaCodec.createEncoderByType(MIME_TYPE);
+            } catch (IOException e) {
+                fail("failed to create " + MIME_TYPE + " encoder");
+            }
             encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
             surface = encoder.createInputSurface();
             encoder.start();
@@ -215,7 +281,11 @@
         Surface surface = null;
 
         try {
-            encoder = MediaCodec.createEncoderByType(MIME_TYPE);
+            try {
+                encoder = MediaCodec.createEncoderByType(MIME_TYPE);
+            } catch (IOException e) {
+                fail("failed to create " + MIME_TYPE + " encoder");
+            }
             encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
             surface = encoder.createInputSurface();
             encoder.start();
@@ -365,11 +435,19 @@
         MediaCodec audioDecoderA = null;
         MediaCodec audioDecoderB = null;
         try {
-            audioDecoderA = MediaCodec.createDecoderByType(MIME_TYPE_AUDIO);
+            try {
+                audioDecoderA = MediaCodec.createDecoderByType(MIME_TYPE_AUDIO);
+            } catch (IOException e) {
+                fail("failed to create first " + MIME_TYPE_AUDIO + " decoder");
+            }
             audioDecoderA.configure(format, null, null, 0);
             audioDecoderA.start();
 
-            audioDecoderB = MediaCodec.createDecoderByType(MIME_TYPE_AUDIO);
+            try {
+                audioDecoderB = MediaCodec.createDecoderByType(MIME_TYPE_AUDIO);
+            } catch (IOException e) {
+                fail("failed to create second " + MIME_TYPE_AUDIO + " decoder");
+            }
             audioDecoderB.configure(format, null, null, 0);
             audioDecoderB.start();
         } finally {
@@ -407,11 +485,19 @@
         MediaCodec audioEncoder = null;
         MediaCodec audioDecoder = null;
         try {
-            audioEncoder = MediaCodec.createEncoderByType(MIME_TYPE_AUDIO);
+            try {
+                audioEncoder = MediaCodec.createEncoderByType(MIME_TYPE_AUDIO);
+            } catch (IOException e) {
+                fail("failed to create " + MIME_TYPE_AUDIO + " encoder");
+            }
             audioEncoder.configure(encoderFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
             audioEncoder.start();
 
-            audioDecoder = MediaCodec.createDecoderByType(MIME_TYPE_AUDIO);
+            try {
+                audioDecoder = MediaCodec.createDecoderByType(MIME_TYPE_AUDIO);
+            } catch (IOException e) {
+                fail("failed to create " + MIME_TYPE_AUDIO + " decoder");
+            }
             audioDecoder.configure(decoderFormat, null, null, 0);
             audioDecoder.start();
         } finally {
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
index 0267497..03a5cfd 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
@@ -886,7 +886,7 @@
         mMediaPlayer.setOnTimedTextListener(new MediaPlayer.OnTimedTextListener() {
             @Override
             public void onTimedText(MediaPlayer mp, TimedText text) {
-                final int toleranceMs = 100;
+                final int toleranceMs = 150;
                 final int durationMs = 500;
                 int posMs = mMediaPlayer.getCurrentPosition();
                 if (text != null) {
diff --git a/tests/tests/media/src/android/media/cts/MediaScannerTest.java b/tests/tests/media/src/android/media/cts/MediaScannerTest.java
index 2dafdc5..f873fbc 100644
--- a/tests/tests/media/src/android/media/cts/MediaScannerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaScannerTest.java
@@ -367,6 +367,120 @@
         assertTrue(new File(path2).delete());
     }
 
+    static class MediaScanEntry {
+        MediaScanEntry(int r, String[] t) {
+            this.res = r;
+            this.tags = t;
+        }
+        int res;
+        String[] tags;
+    }
+
+    MediaScanEntry encodingtestfiles[] = {
+            new MediaScanEntry(R.raw.gb18030_1,
+                    new String[] {"罗志祥", "2009年11月新歌", "罗志祥", "爱不单行(TV Version)", null} ),
+            new MediaScanEntry(R.raw.gb18030_2,
+                    new String[] {"张杰", "明天过后", null, "明天过后", null} ),
+            new MediaScanEntry(R.raw.gb18030_3,
+                    new String[] {"电视原声带", "格斗天王(限量精装版)(预购版)", null, "11.Open Arms.( cn808.net )", null} ),
+            new MediaScanEntry(R.raw.gb18030_4,
+                    new String[] {"莫扎特", "黄金古典", "柏林爱乐乐团", "第25号交响曲", "莫扎特"} ),
+            new MediaScanEntry(R.raw.gb18030_5,
+                    new String[] {"光良", "童话", "光良", "02.童话", "鍏夎壇"} ),
+            new MediaScanEntry(R.raw.gb18030_6,
+                    new String[] {"张韶涵", "潘朵拉", "張韶涵", "隐形的翅膀", "王雅君"} ),
+            new MediaScanEntry(R.raw.gb18030_7,
+                    new String[] {"五月天", "后青春期的诗", null, "突然好想你", null} ),
+            new MediaScanEntry(R.raw.gb18030_8,
+                    new String[] {"周杰伦", "Jay", null, "反方向的钟", null} ),
+            new MediaScanEntry(R.raw.big5_1,
+                    new String[] {"蘇永康", "So I Sing 08 Live", "蘇永康", "囍帖街", null} ),
+            new MediaScanEntry(R.raw.big5_2,
+                    new String[] {"蘇永康", "So I Sing 08 Live", "蘇永康", "從不喜歡孤單一個 - 蘇永康/吳雨霏", null} ),
+            new MediaScanEntry(R.raw.cp1251_v1,
+                    new String[] {"Екатерина Железнова", "Корабль игрушек", null, "Раз, два, три", null} ),
+            new MediaScanEntry(R.raw.cp1251_v1v2,
+                    new String[] {"Мельница", "Перевал", null, "Королевна", null} ),
+            new MediaScanEntry(R.raw.cp1251_3,
+                    new String[] {"Тату (tATu)", "200 По Встречной [Limited edi", null, "Я Сошла С Ума", null} ),
+            new MediaScanEntry(R.raw.cp1251_4,
+                    new String[] {"Александр Розенбаум", "Философия любви", null, "Разговор в гостинице (Как жить без веры)", "А.Розенбаум"} ),
+            new MediaScanEntry(R.raw.cp1251_5,
+                    new String[] {"Александр Розенбаум", "Философия любви", null, "Четвертиночка", "А.Розенбаум"} ),
+            new MediaScanEntry(R.raw.cp1251_6,
+                    new String[] {"Александр Розенбаум", "Философия ремесла", null, "Ну, вот...", "А.Розенбаум"} ),
+            new MediaScanEntry(R.raw.shiftjis1,
+                    new String[] {"", "", null, "中島敦「山月記」(第1回)", null} ),
+            new MediaScanEntry(R.raw.shiftjis2,
+                    new String[] {"音人", "SoundEffects", null, "ファンファーレ", null} ),
+            new MediaScanEntry(R.raw.shiftjis3,
+                    new String[] {"音人", "SoundEffects", null, "シンキングタイム", null} ),
+            new MediaScanEntry(R.raw.shiftjis4,
+                    new String[] {"音人", "SoundEffects", null, "出題", null} ),
+            new MediaScanEntry(R.raw.shiftjis5,
+                    new String[] {"音人", "SoundEffects", null, "時報", null} ),
+            new MediaScanEntry(R.raw.shiftjis6,
+                    new String[] {"音人", "SoundEffects", null, "正解", null} ),
+            new MediaScanEntry(R.raw.shiftjis7,
+                    new String[] {"音人", "SoundEffects", null, "残念", null} ),
+            new MediaScanEntry(R.raw.shiftjis8,
+                    new String[] {"音人", "SoundEffects", null, "間違い", null} ),
+            new MediaScanEntry(R.raw.iso88591_1,
+                    new String[] {"Mozart", "Best of Mozart", null, "Overtüre (Die Hochzeit des Figaro)", null} ),
+            new MediaScanEntry(R.raw.hebrew,
+                    new String[] {"אריק סיני", "", null, "לי ולך", null } ),
+            new MediaScanEntry(R.raw.hebrew2,
+                    new String[] {"הפרוייקט של עידן רייכל", "Untitled - 11-11-02 (9)", null, "בואי", null } )
+    };
+
+    public void testEncodingDetection() throws Exception {
+        for (int i = 0; i< encodingtestfiles.length; i++) {
+            MediaScanEntry entry = encodingtestfiles[i];
+            String path =  mFileDir + "/" + "encodingtest" + i + ".mp3";
+            writeFile(entry.res, path);
+        }
+
+        startMediaScanAndWait();
+
+        String columns[] = {
+                MediaStore.Audio.Media.ARTIST,
+                MediaStore.Audio.Media.ALBUM,
+                MediaStore.Audio.Media.ALBUM_ARTIST,
+                MediaStore.Audio.Media.TITLE,
+                MediaStore.Audio.Media.COMPOSER
+        };
+        ContentResolver res = mContext.getContentResolver();
+        for (int i = 0; i< encodingtestfiles.length; i++) {
+            MediaScanEntry entry = encodingtestfiles[i];
+            String path =  mFileDir + "/" + "encodingtest" + i + ".mp3";
+            Cursor c = res.query(MediaStore.Audio.Media.getContentUri("external"), columns,
+                    MediaStore.Audio.Media.DATA + "=?", new String[] {path}, null);
+            assertNotNull("null cursor", c);
+            assertEquals("wrong number or results", 1, c.getCount());
+            assertTrue("failed to move cursor", c.moveToFirst());
+
+            for (int j =0; j < 5; j++) {
+                String expected = entry.tags[j];
+                if ("".equals(expected)) {
+                    // empty entry in the table means an unset id3 tag that is filled in by
+                    // the media scanner, e.g. by using "<unknown>". Since this may be localized,
+                    // don't check it for any particular value.
+                    assertNotNull("unexpected null entry " + i + " field " + j + "(" + path + ")",
+                            c.getString(j));
+                } else {
+                    assertEquals("mismatch on entry " + i + " field " + j + "(" + path + ")",
+                            expected, c.getString(j));
+                }
+            }
+            // clean up
+            new File(path).delete();
+            res.delete(MediaStore.Audio.Media.getContentUri("external"),
+                    MediaStore.Audio.Media.DATA + "=?", new String[] {path});
+
+            c.close();
+        }
+    }
+
     private void startMediaScanAndWait() throws InterruptedException {
         ScannerNotificationReceiver finishedReceiver = new ScannerNotificationReceiver(
                 Intent.ACTION_MEDIA_SCANNER_FINISHED);
diff --git a/tests/tests/media/src/android/media/cts/Vp8EncoderTest.java b/tests/tests/media/src/android/media/cts/Vp8EncoderTest.java
index 14e4055..8091d31 100644
--- a/tests/tests/media/src/android/media/cts/Vp8EncoderTest.java
+++ b/tests/tests/media/src/android/media/cts/Vp8EncoderTest.java
@@ -286,7 +286,7 @@
                 if (result >= 0) {
                     int outputBufIndex = result;
                     byte[] buffer = new byte[mBufferInfo.size];
-                    mOutputBuffers[outputBufIndex].rewind();
+                    mOutputBuffers[outputBufIndex].position(mBufferInfo.offset);
                     mOutputBuffers[outputBufIndex].get(buffer, 0, mBufferInfo.size);
 
                     if ((outputFrameIndex == 0)
diff --git a/tests/tests/net/Android.mk b/tests/tests/net/Android.mk
index 82abd62..1653335 100644
--- a/tests/tests/net/Android.mk
+++ b/tests/tests/net/Android.mk
@@ -21,7 +21,7 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner voip-common
+LOCAL_JAVA_LIBRARIES := android.test.runner voip-common conscrypt
 
 LOCAL_JNI_SHARED_LIBRARIES := libnativedns_jni
 
diff --git a/tests/tests/net/src/android/net/http/cts/X509TrustManagerExtensionsTest.java b/tests/tests/net/src/android/net/http/cts/X509TrustManagerExtensionsTest.java
new file mode 100644
index 0000000..9c0d774
--- /dev/null
+++ b/tests/tests/net/src/android/net/http/cts/X509TrustManagerExtensionsTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.net.http.cts;
+
+import android.net.http.X509TrustManagerExtensions;
+import android.util.Base64;
+
+import java.io.File;
+import java.io.ByteArrayInputStream;
+
+import java.security.KeyStore;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
+import junit.framework.TestCase;
+
+import com.android.org.conscrypt.TrustedCertificateStore;
+import com.android.org.conscrypt.TrustManagerImpl;
+
+public class X509TrustManagerExtensionsTest extends TestCase {
+
+    public void testIsUserAddedCert() throws Exception {
+        final String testCert =
+            "MIICfjCCAeegAwIBAgIJAMefIzKHY5H4MA0GCSqGSIb3DQEBBQUAMFgxCzAJBgNV" +
+            "BAYTAlVTMQswCQYDVQQIDAJDQTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEPMA0G" +
+            "A1UECgwGR2V3Z3VsMRMwEQYDVQQDDApnZXdndWwuY29tMB4XDTEzMTEwNTAwNDE0" +
+            "MFoXDTEzMTIwNTAwNDE0MFowWDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRYw" +
+            "FAYDVQQHDA1Nb3VudGFpbiBWaWV3MQ8wDQYDVQQKDAZHZXdndWwxEzARBgNVBAMM" +
+            "Cmdld2d1bC5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKpc/I0Ss4sm" +
+            "yV2iX5xRMM7+XXAhiWrceGair4MpvDrGIa1kFj2phtx4IqTfDnNU7AhRJYkDYmJQ" +
+            "fUJ8i6F+I08uNiGVO4DtPJbZcBXg9ME9EMaJCslm995ueeNWSw1Ky8zM0tt4p+94" +
+            "BcXJ7PC3N2WgkvtE8xwNbaeUfhGPzJKXAgMBAAGjUDBOMB0GA1UdDgQWBBQQ/iW7" +
+            "JCkSI2sbn4nTBiZ9PSiO8zAfBgNVHSMEGDAWgBQQ/iW7JCkSI2sbn4nTBiZ9PSiO" +
+            "8zAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBABQBrUOWTCSIl3vkRR3w" +
+            "3bPzh3BpqDmxH9xe4rZr+MVKKjpGjY1z2m2EEtyNz3tbgVQym5+si00DUHFL0IP1" +
+            "SuRULmPyEpTBVbV+PA5Kc967ZcDgYt4JtdMcCeKbIFaU6r8oEYEL2PTlNZmgbunM" +
+            "pXktkhVvNxZeSa8yM9bPhXkN";
+
+        CertificateFactory cf = CertificateFactory.getInstance("X.509");
+        X509Certificate cert = (X509Certificate)cf.generateCertificate(
+            new ByteArrayInputStream(Base64.decode(testCert, Base64.DEFAULT)));
+
+        // Test without adding cert to keystore.
+        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+        X509TrustManagerExtensions tmeNegative =
+            new X509TrustManagerExtensions(new TrustManagerImpl(keyStore));
+        assertEquals(false, tmeNegative.isUserAddedCertificate(cert));
+
+        // Test with cert added to keystore.
+        final File DIR_TEMP = new File(System.getProperty("java.io.tmpdir"));
+        final File DIR_TEST = new File(DIR_TEMP, "test");
+        final File system = new File(DIR_TEST, "system-test");
+        final File added = new File(DIR_TEST, "added-test");
+        final File deleted = new File(DIR_TEST, "deleted-test");
+
+        TrustedCertificateStore tcs = new TrustedCertificateStore(system, added, deleted);
+        added.mkdirs();
+        tcs.installCertificate(cert);
+        X509TrustManagerExtensions tmePositive =
+            new X509TrustManagerExtensions(new TrustManagerImpl(keyStore, null, tcs));
+        assertEquals(true, tmePositive.isUserAddedCertificate(cert));
+    }
+}
diff --git a/tests/tests/permission/Android.mk b/tests/tests/permission/Android.mk
index 07f20d8..a32cc04 100644
--- a/tests/tests/permission/Android.mk
+++ b/tests/tests/permission/Android.mk
@@ -21,7 +21,7 @@
 
 LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner guava
 
 LOCAL_JNI_SHARED_LIBRARIES := libctspermission_jni
 
diff --git a/tests/tests/permission/jni/android_permission_cts_FileUtils.cpp b/tests/tests/permission/jni/android_permission_cts_FileUtils.cpp
index 272bbdc..8f32027 100644
--- a/tests/tests/permission/jni/android_permission_cts_FileUtils.cpp
+++ b/tests/tests/permission/jni/android_permission_cts_FileUtils.cpp
@@ -25,6 +25,9 @@
 #include <grp.h>
 #include <pwd.h>
 #include <string.h>
+#include <ScopedLocalRef.h>
+#include <ScopedPrimitiveArray.h>
+#include <ScopedUtfChars.h>
 
 static jfieldID gFileStatusDevFieldID;
 static jfieldID gFileStatusInoFieldID;
@@ -46,14 +49,15 @@
  * Copied from hidden API: frameworks/base/core/jni/android_os_FileUtils.cpp
  */
 
-jboolean android_permission_cts_FileUtils_getFileStatus(JNIEnv* env, jobject thiz,
-        jstring path, jobject fileStatus, jboolean statLinks)
+jboolean android_permission_cts_FileUtils_getFileStatus(JNIEnv* env,
+        jobject /* thiz */, jstring path, jobject fileStatus, jboolean statLinks)
 {
-    const char* pathStr = env->GetStringUTFChars(path, NULL);
+    ScopedUtfChars cPath(env, path);
     jboolean ret = false;
     struct stat s;
 
-    int res = statLinks == true ? lstat(pathStr, &s) : stat(pathStr, &s);
+    int res = statLinks == true ? lstat(cPath.c_str(), &s)
+            : stat(cPath.c_str(), &s);
 
     if (res == 0) {
         ret = true;
@@ -73,20 +77,18 @@
         }
     }
 
-    env->ReleaseStringUTFChars(path, pathStr);
-
     return ret;
 }
 
-jstring android_permission_cts_FileUtils_getUserName(JNIEnv* env, jobject thiz,
-        jint uid)
+jstring android_permission_cts_FileUtils_getUserName(JNIEnv* env,
+        jobject /* thiz */, jint uid)
 {
     struct passwd *pwd = getpwuid(uid);
     return env->NewStringUTF(pwd->pw_name);
 }
 
-jstring android_permission_cts_FileUtils_getGroupName(JNIEnv* env, jobject thiz,
-        jint gid)
+jstring android_permission_cts_FileUtils_getGroupName(JNIEnv* env,
+        jobject /* thiz */, jint gid)
 {
     struct group *grp = getgrgid(gid);
     return env->NewStringUTF(grp->gr_name);
@@ -94,42 +96,106 @@
 
 static jboolean isPermittedCapBitSet(JNIEnv* env, jstring path, size_t capId)
 {
-    const char* pathStr = env->GetStringUTFChars(path, NULL);
-    jboolean ret = false;
-
     struct vfs_cap_data capData;
     memset(&capData, 0, sizeof(capData));
 
-    ssize_t result = getxattr(pathStr, XATTR_NAME_CAPS, &capData,
+    ScopedUtfChars cPath(env, path);
+    ssize_t result = getxattr(cPath.c_str(), XATTR_NAME_CAPS, &capData,
                               sizeof(capData));
-    if (result > 0) {
-      ret = (capData.data[CAP_TO_INDEX(capId)].permitted &
-             CAP_TO_MASK(capId)) != 0;
-      ALOGD("isPermittedCapBitSet(): getxattr(\"%s\") call succeeded, "
-            "cap bit %u %s",
-            pathStr, capId, ret ? "set" : "unset");
-    } else {
-      ALOGD("isPermittedCapBitSet(): getxattr(\"%s\") call failed: "
-            "return %d (error: %s (%d))\n",
-            pathStr, result, strerror(errno), errno);
+    if (result <= 0)
+    {
+          ALOGD("isPermittedCapBitSet(): getxattr(\"%s\") call failed: "
+                  "return %d (error: %s (%d))\n",
+                  cPath.c_str(), result, strerror(errno), errno);
+          return false;
     }
 
-    env->ReleaseStringUTFChars(path, pathStr);
-    return ret;
+    return (capData.data[CAP_TO_INDEX(capId)].permitted &
+            CAP_TO_MASK(capId)) != 0;
 }
 
 jboolean android_permission_cts_FileUtils_hasSetUidCapability(JNIEnv* env,
-        jobject clazz, jstring path)
+        jobject /* clazz */, jstring path)
 {
     return isPermittedCapBitSet(env, path, CAP_SETUID);
 }
 
 jboolean android_permission_cts_FileUtils_hasSetGidCapability(JNIEnv* env,
-        jobject clazz, jstring path)
+        jobject /* clazz */, jstring path)
 {
     return isPermittedCapBitSet(env, path, CAP_SETGID);
 }
 
+static bool throwNamedException(JNIEnv* env, const char* className,
+        const char* message)
+{
+    ScopedLocalRef<jclass> eClazz(env, env->FindClass(className));
+    if (eClazz.get() == NULL)
+    {
+        ALOGE("throwNamedException(): failed to find class %s, cannot throw",
+                className);
+        return false;
+    }
+
+    env->ThrowNew(eClazz.get(), message);
+    return true;
+}
+
+// fill vfs_cap_data's permitted caps given a Java int[] of cap ids
+static bool fillPermittedCaps(vfs_cap_data* capData, JNIEnv* env, jintArray capIds)
+{
+    ScopedIntArrayRO cCapIds(env, capIds);
+    const size_t capCount = cCapIds.size();
+
+    for (size_t i = 0; i < capCount; ++i)
+    {
+        const jint capId = cCapIds[i];
+        if (!cap_valid(capId))
+        {
+            char message[64];
+            snprintf(message, sizeof(message),
+                    "capability id %d out of valid range", capId);
+            throwNamedException(env, "java/lang/IllegalArgumentException",
+                    message);
+
+            return false;
+        }
+        capData->data[CAP_TO_INDEX(capId)].permitted |= CAP_TO_MASK(capId);
+    }
+    return true;
+}
+
+jboolean android_permission_cts_FileUtils_CapabilitySet_fileHasOnly(JNIEnv* env,
+        jobject /* clazz */, jstring path, jintArray capIds)
+{
+    struct vfs_cap_data expectedCapData;
+    memset(&expectedCapData, 0, sizeof(expectedCapData));
+
+    expectedCapData.magic_etc = VFS_CAP_REVISION | VFS_CAP_FLAGS_EFFECTIVE;
+    if (!fillPermittedCaps(&expectedCapData, env, capIds))
+    {
+        // exception thrown
+        return false;
+    }
+
+    struct vfs_cap_data actualCapData;
+    memset(&actualCapData, 0, sizeof(actualCapData));
+
+    ScopedUtfChars cPath(env, path);
+    ssize_t result = getxattr(cPath.c_str(), XATTR_NAME_CAPS, &actualCapData,
+            sizeof(actualCapData));
+    if (result <= 0)
+    {
+        ALOGD("fileHasOnly(): getxattr(\"%s\") call failed: "
+                "return %d (error: %s (%d))\n",
+                cPath.c_str(), result, strerror(errno), errno);
+        return false;
+    }
+
+    return (memcmp(&expectedCapData, &actualCapData,
+            sizeof(struct vfs_cap_data)) == 0);
+}
+
 static JNINativeMethod gMethods[] = {
     {  "getFileStatus", "(Ljava/lang/String;Landroid/permission/cts/FileUtils$FileStatus;Z)Z",
             (void *) android_permission_cts_FileUtils_getFileStatus  },
@@ -143,6 +209,11 @@
             (void *) android_permission_cts_FileUtils_hasSetGidCapability   },
 };
 
+static JNINativeMethod gCapabilitySetMethods[] = {
+    {  "fileHasOnly", "(Ljava/lang/String;[I)Z",
+            (void *) android_permission_cts_FileUtils_CapabilitySet_fileHasOnly  },
+};
+
 int register_android_permission_cts_FileUtils(JNIEnv* env)
 {
     jclass clazz = env->FindClass("android/permission/cts/FileUtils");
@@ -161,6 +232,16 @@
     gFileStatusMtimeFieldID = env->GetFieldID(fileStatusClass, "mtime", "J");
     gFileStatusCtimeFieldID = env->GetFieldID(fileStatusClass, "ctime", "J");
 
-    return env->RegisterNatives(clazz, gMethods, 
-            sizeof(gMethods) / sizeof(JNINativeMethod)); 
+    jint result = env->RegisterNatives(clazz, gMethods,
+            sizeof(gMethods) / sizeof(JNINativeMethod));
+    if (result)
+    {
+      return result;
+    }
+
+    // register FileUtils.CapabilitySet native methods
+    jclass capClazz = env->FindClass("android/permission/cts/FileUtils$CapabilitySet");
+
+    return env->RegisterNatives(capClazz, gCapabilitySetMethods,
+            sizeof(gCapabilitySetMethods) / sizeof(JNINativeMethod));
 }
diff --git a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
index 83f2a36..27c7215 100644
--- a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
@@ -44,6 +44,8 @@
 import java.util.List;
 import java.util.Set;
 
+import libcore.io.OsConstants;
+
 /**
  * Verify certain permissions on the filesystem
  *
@@ -823,6 +825,30 @@
         assertFileOwnedByGroup(f, "system");
     }
 
+    public void testFileHasOnlyCapsThrowsOnInvalidCaps() throws Exception {
+        try {
+            // Ensure negative cap id fails.
+            new FileUtils.CapabilitySet()
+                    .add(-1)
+                    .fileHasOnly("/system/bin/run-as");
+            fail();
+        }
+        catch (IllegalArgumentException e) {
+            // expected
+        }
+
+        try {
+            // Ensure too-large cap throws.
+            new FileUtils.CapabilitySet()
+                    .add(OsConstants.CAP_LAST_CAP + 1)
+                    .fileHasOnly("/system/bin/run-as");
+            fail();
+        }
+        catch (IllegalArgumentException e) {
+            // expected
+        }
+    }
+
     /**
      * Test that the /system/bin/run-as command has setuid and setgid
      * attributes set on the file.  If these calls fail, debugger
@@ -845,6 +871,12 @@
         // ensure file has setuid/setgid enabled
         assertTrue(FileUtils.hasSetUidCapability(filename));
         assertTrue(FileUtils.hasSetGidCapability(filename));
+
+        // ensure file has *only* setuid/setgid attributes enabled
+        assertTrue(new FileUtils.CapabilitySet()
+                .add(OsConstants.CAP_SETUID)
+                .add(OsConstants.CAP_SETGID)
+                .fileHasOnly("/system/bin/run-as"));
     }
 
     private static Set<File>
diff --git a/tests/tests/permission/src/android/permission/cts/FileUtils.java b/tests/tests/permission/src/android/permission/cts/FileUtils.java
index 9cd4999..657c783 100644
--- a/tests/tests/permission/src/android/permission/cts/FileUtils.java
+++ b/tests/tests/permission/src/android/permission/cts/FileUtils.java
@@ -16,6 +16,13 @@
  * limitations under the License.
  */
 
+import com.google.common.primitives.Ints;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import libcore.io.OsConstants;
+
 /** Bits and pieces copied from hidden API of android.os.FileUtils. */
 public class FileUtils {
 
@@ -82,6 +89,27 @@
         }
     }
 
+    public static class CapabilitySet {
+
+        private final Set<Integer> mCapabilities = new HashSet<Integer>();
+
+        public CapabilitySet add(int capability) {
+            if ((capability < 0) || (capability > OsConstants.CAP_LAST_CAP)) {
+                throw new IllegalArgumentException(String.format(
+                        "capability id %d out of valid range", capability));
+            }
+            mCapabilities.add(capability);
+            return this;
+        }
+
+        private native static boolean fileHasOnly(String path,
+                int[] capabilities);
+
+        public boolean fileHasOnly(String path) {
+            return fileHasOnly(path, Ints.toArray(mCapabilities));
+        }
+    }
+
     /**
      * @param path of the file to stat
      * @param status object to set the fields on
diff --git a/tests/tests/print/Android.mk b/tests/tests/print/Android.mk
new file mode 100644
index 0000000..d9fcba6
--- /dev/null
+++ b/tests/tests/print/Android.mk
@@ -0,0 +1,33 @@
+# Copyright (C) 2014 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_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+    src/android/print/cts/IPrivilegedOperations.aidl
+
+LOCAL_PACKAGE_NAME := CtsPrintTestCases
+
+LOCAL_STATIC_JAVA_LIBRARIES := mockito-target ctstestrunner ub-uiautomator
+
+# This test runner sets up/cleans up the device before/after running the tests.
+LOCAL_CTS_TEST_RUNNER := com.android.cts.tradefed.testtype.PrintTestRunner
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/print/AndroidManifest.xml b/tests/tests/print/AndroidManifest.xml
new file mode 100644
index 0000000..4c94fd5
--- /dev/null
+++ b/tests/tests/print/AndroidManifest.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+    Copyright (C) 2014 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.print">
+
+    <application android:allowBackup="false" >
+
+        <uses-library android:name="android.test.runner"/>
+
+        <activity android:name="android.print.cts.PrintDocumentActivity"/>
+
+        <service
+            android:name="android.print.cts.services.FirstPrintService"
+            android:permission="android.permission.BIND_PRINT_SERVICE">
+            <intent-filter>
+                <action android:name="android.printservice.PrintService" />
+            </intent-filter>
+            <meta-data
+               android:name="android.printservice"
+               android:resource="@xml/printservice">
+            </meta-data>
+        </service>
+
+        <service
+            android:name="android.print.cts.services.SecondPrintService"
+            android:permission="android.permission.BIND_PRINT_SERVICE">
+            <intent-filter>
+                <action android:name="android.printservice.PrintService" />
+            </intent-filter>
+            <meta-data
+               android:name="android.printservice"
+               android:resource="@xml/printservice">
+            </meta-data>
+        </service>
+
+        <activity
+            android:name="android.print.cts.services.SettingsActivity"
+            android:permission="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY"
+            android:exported="true">
+        </activity>
+
+        <activity
+            android:name="android.print.cts.services.AddPrintersActivity"
+            android:permission="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY"
+            android:exported="true">
+        </activity>
+
+        <activity
+            android:name="android.print.cts.services.CustomPrintOptionsActivity"
+            android:permission="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY"
+            android:exported="true">
+        </activity>
+
+  </application>
+
+  <instrumentation android:name="android.support.test.uiautomator.UiAutomatorInstrumentationTestRunner"
+          android:targetPackage="com.android.cts.print"
+          android:label="Tests for the print APIs."/>
+
+</manifest>
diff --git a/tests/tests/print/res/values/strings.xml b/tests/tests/print/res/values/strings.xml
new file mode 100644
index 0000000..6d869e9
--- /dev/null
+++ b/tests/tests/print/res/values/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+    Copyright (C) 2014 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="resolution_200x200">200x200</string>
+    <string name="resolution_300x300">300x300</string>
+    <string name="resolution_600x600">600x600</string>
+
+</resources>
diff --git a/tests/tests/print/res/xml/printservice.xml b/tests/tests/print/res/xml/printservice.xml
new file mode 100644
index 0000000..5579b81
--- /dev/null
+++ b/tests/tests/print/res/xml/printservice.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+    Copyright (C) 2014 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.
+-->
+
+<print-service  xmlns:android="http://schemas.android.com/apk/res/android"
+     android:settingsActivity="android.print.services.SettingsActivity"
+     android:addPrintersActivity="android.print.services.AddPrintersActivity"
+     android:advancedPrintOptionsActivity="android.print.services.CustomPrintOptionsActivity"/>
diff --git a/tests/tests/print/src/android/print/cts/BasePrintTest.java b/tests/tests/print/src/android/print/cts/BasePrintTest.java
new file mode 100644
index 0000000..d193bb0
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/BasePrintTest.java
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2014 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.print.cts;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
+import android.print.PageRange;
+import android.print.PrintAttributes;
+import android.print.PrintDocumentAdapter;
+import android.print.PrintDocumentAdapter.LayoutResultCallback;
+import android.print.PrintDocumentAdapter.WriteResultCallback;
+import android.print.PrintManager;
+import android.print.PrinterId;
+import android.print.cts.services.PrintServiceCallbacks;
+import android.print.cts.services.PrinterDiscoverySessionCallbacks;
+import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.printservice.PrintJob;
+import android.printservice.PrintService;
+import android.support.test.uiautomator.UiAutomatorTestCase;
+import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.support.test.uiautomator.UiSelector;
+import android.util.DisplayMetrics;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.mockito.InOrder;
+import org.mockito.stubbing.Answer;
+
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * This is the base class for print tests.
+ */
+public abstract class BasePrintTest extends UiAutomatorTestCase {
+
+    private static final long OPERATION_TIMEOUT = 10000;
+
+    private static final String ARG_PRIVILEGED_OPS = "ARG_PRIVILEGED_OPS";
+
+    private static final String PRINT_SPOOLER_PACKAGE_NAME = "com.android.printspooler";
+
+    protected static final String PRINT_JOB_NAME = "Test";
+
+    private PrintDocumentActivity mActivity;
+
+    private Locale mOldLocale;
+
+    private CallCounter mLayoutCallCounter;
+    private CallCounter mWriteCallCounter;
+    private CallCounter mFinishCallCounter;
+    private CallCounter mPrintJobQueuedCallCounter;
+    private CallCounter mDestroySessionCallCounter;
+
+    @Override
+    public void setUp() throws Exception {
+        // Make sure we start with a clean slate.
+        clearPrintSpoolerData();
+
+        // Workaround for dexmaker bug: https://code.google.com/p/dexmaker/issues/detail?id=2
+        // Dexmaker is used by mockito.
+        System.setProperty("dexmaker.dexcache", getInstrumentation()
+                .getTargetContext().getCacheDir().getPath());
+
+        // Set to US locale.
+        Resources resources = getInstrumentation().getTargetContext().getResources();
+        Configuration oldConfiguration = resources.getConfiguration();
+        if (!oldConfiguration.locale.equals(Locale.US)) {
+            mOldLocale = oldConfiguration.locale;
+            DisplayMetrics displayMetrics = resources.getDisplayMetrics();
+            Configuration newConfiguration = new Configuration(oldConfiguration);
+            newConfiguration.locale = Locale.US;
+            resources.updateConfiguration(newConfiguration, displayMetrics);
+        }
+
+        // Initialize the latches.
+        mLayoutCallCounter = new CallCounter();
+        mFinishCallCounter = new CallCounter();
+        mWriteCallCounter = new CallCounter();
+        mFinishCallCounter = new CallCounter();
+        mPrintJobQueuedCallCounter = new CallCounter();
+        mDestroySessionCallCounter = new CallCounter();
+
+        // Create the activity for the right locale.
+        createActivity();
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        // Done with the activity.
+        getActivity().finish();
+
+        // Restore the locale if needed.
+        if (mOldLocale != null) {
+            Resources resources = getInstrumentation().getTargetContext().getResources();
+            DisplayMetrics displayMetrics = resources.getDisplayMetrics();
+            Configuration newConfiguration = new Configuration(resources.getConfiguration());
+            newConfiguration.locale = mOldLocale;
+            mOldLocale = null;
+            resources.updateConfiguration(newConfiguration, displayMetrics);
+        }
+
+        // Make sure the spooler is cleaned.
+        clearPrintSpoolerData();
+    }
+
+    protected void print(final PrintDocumentAdapter adapter) {
+        // Initiate printing as if coming from the app.
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                PrintManager printManager = (PrintManager) getActivity()
+                        .getSystemService(Context.PRINT_SERVICE);
+                printManager.print("Print job", adapter, null);
+            }
+        });
+    }
+
+    protected void onLayoutCalled() {
+        mLayoutCallCounter.call();
+    }
+
+    protected void onWriteCalled() {
+        mWriteCallCounter.call();
+    }
+
+    protected void onFinishCalled() {
+        mFinishCallCounter.call();
+    }
+
+    protected void onPrintJobQueuedCalled() {
+        mPrintJobQueuedCallCounter.call();
+    }
+
+    protected void onPrinterDiscoverySessionDestroyCalled() {
+        mDestroySessionCallCounter.call();
+    }
+
+    protected void waitForPrinterDiscoverySessionDestroyCallbackCalled() {
+        waitForCallbackCallCount(mDestroySessionCallCounter, 1,
+                "Did not get expected call to onDestroyPrinterDiscoverySession.");
+    }
+
+    protected void waitForServiceOnPrintJobQueuedCallbackCalled() {
+        waitForCallbackCallCount(mPrintJobQueuedCallCounter, 1,
+                "Did not get expected call to onPrintJobQueued.");
+    }
+
+    protected void waitForAdapterFinishCallbackCalled() {
+        waitForCallbackCallCount(mFinishCallCounter, 1,
+                "Did not get expected call to finish.");
+    }
+
+    protected void waitForLayoutAdapterCallbackCount(int count) {
+        waitForCallbackCallCount(mLayoutCallCounter, count,
+                "Did not get expected call to layout.");
+    }
+
+    protected void waitForWriteForAdapterCallback() {
+        waitForCallbackCallCount(mWriteCallCounter, 1, "Did not get expected call to write.");
+    }
+
+    private void waitForCallbackCallCount(CallCounter counter, int count, String message) {
+        try {
+            counter.waitForCount(count, OPERATION_TIMEOUT);
+        } catch (TimeoutException te) {
+            fail(message);
+        }
+    }
+
+    protected void selectPrinter(String printerName) throws UiObjectNotFoundException {
+        UiObject destinationSpinner = new UiObject(new UiSelector().resourceId(
+                "com.android.printspooler:id/destination_spinner"));
+        destinationSpinner.click();
+        UiObject printerOption = new UiObject(new UiSelector().text(printerName));
+        printerOption.click();
+    }
+
+    protected void changeOrientation(String orientation) throws UiObjectNotFoundException {
+        UiObject orientationSpinner = new UiObject(new UiSelector().resourceId(
+                "com.android.printspooler:id/orientation_spinner"));
+        orientationSpinner.click();
+        UiObject orientationOption = new UiObject(new UiSelector().text(orientation));
+        orientationOption.click();
+    }
+
+    protected void changeMediaSize(String mediaSize) throws UiObjectNotFoundException {
+        UiObject mediaSizeSpinner = new UiObject(new UiSelector().resourceId(
+                "com.android.printspooler:id/paper_size_spinner"));
+        mediaSizeSpinner.click();
+        UiObject mediaSizeOption = new UiObject(new UiSelector().text(mediaSize));
+        mediaSizeOption.click();
+    }
+
+    protected void changeColor(String color) throws UiObjectNotFoundException {
+        UiObject colorSpinner = new UiObject(new UiSelector().resourceId(
+                "com.android.printspooler:id/color_spinner"));
+        colorSpinner.click();
+        UiObject colorOption = new UiObject(new UiSelector().text(color));
+        colorOption.click();
+    }
+
+    protected void clickPrintButton() throws UiObjectNotFoundException {
+        UiObject printButton = new UiObject(new UiSelector().resourceId(
+                "com.android.printspooler:id/print_button"));
+        printButton.click();
+    }
+
+    protected PrintDocumentActivity getActivity() {
+        return mActivity;
+    }
+
+    private void createActivity() {
+        mActivity = launchActivity(
+                getInstrumentation().getTargetContext().getPackageName(),
+                PrintDocumentActivity.class, null);
+    }
+
+    protected void clearPrintSpoolerData() throws Exception {
+        IPrivilegedOperations privilegedOps = IPrivilegedOperations.Stub.asInterface(
+                getParams().getBinder(ARG_PRIVILEGED_OPS));
+        privilegedOps.clearApplicationUserData(PRINT_SPOOLER_PACKAGE_NAME);
+    }
+
+    protected void verifyLayoutCall(InOrder inOrder, PrintDocumentAdapter mock,
+            PrintAttributes oldAttributes, PrintAttributes newAttributes,
+            final boolean forPreview) {
+        inOrder.verify(mock).onLayout(eq(oldAttributes), eq(newAttributes),
+                any(CancellationSignal.class), any(LayoutResultCallback.class), argThat(
+                        new BaseMatcher<Bundle>() {
+                            @Override
+                            public boolean matches(Object item) {
+                                Bundle bundle = (Bundle) item;
+                                return forPreview == bundle.getBoolean(
+                                        PrintDocumentAdapter.EXTRA_PRINT_PREVIEW);
+                            }
+
+                            @Override
+                            public void describeTo(Description description) {
+                                /* do nothing */
+                            }
+                        }));
+    }
+
+    protected PrintDocumentAdapter createMockPrintDocumentAdapter(Answer<Void> layoutAnswer,
+            Answer<Void> writeAnswer, Answer<Void> finishAnswer) {
+        // Create a mock print adapter.
+        PrintDocumentAdapter adapter = mock(PrintDocumentAdapter.class);
+        if (layoutAnswer != null) {
+            doAnswer(layoutAnswer).when(adapter).onLayout(any(PrintAttributes.class),
+                    any(PrintAttributes.class), any(CancellationSignal.class),
+                    any(LayoutResultCallback.class), any(Bundle.class));
+        }
+        if (writeAnswer != null) {
+            doAnswer(writeAnswer).when(adapter).onWrite(any(PageRange[].class),
+                    any(ParcelFileDescriptor.class), any(CancellationSignal.class),
+                    any(WriteResultCallback.class));
+        }
+        if (finishAnswer != null) {
+            doAnswer(finishAnswer).when(adapter).onFinish();
+        }
+        return adapter;
+    }
+
+    @SuppressWarnings("unchecked")
+    protected PrinterDiscoverySessionCallbacks createMockPrinterDiscoverySessionCallbacks(
+            Answer<Void> onStartPrinterDiscovery, Answer<Void> onStopPrinterDiscovery,
+            Answer<Void> onValidatePrinters, Answer<Void> onStartPrinterStateTracking,
+            Answer<Void> onStopPrinterStateTracking, Answer<Void> onDestroy) {
+        PrinterDiscoverySessionCallbacks callbacks = mock(PrinterDiscoverySessionCallbacks.class);
+
+        doCallRealMethod().when(callbacks).setSession(any(StubbablePrinterDiscoverySession.class));
+        when(callbacks.getSession()).thenCallRealMethod();
+
+        if (onStartPrinterDiscovery != null) {
+            doAnswer(onStartPrinterDiscovery).when(callbacks).onStartPrinterDiscovery(
+                    any(List.class));
+        }
+        if (onStopPrinterDiscovery != null) {
+            doAnswer(onStopPrinterDiscovery).when(callbacks).onStopPrinterDiscovery();
+        }
+        if (onValidatePrinters != null) {
+            doAnswer(onValidatePrinters).when(callbacks).onValidatePrinters(
+                    any(List.class));
+        }
+        if (onStartPrinterStateTracking != null) {
+            doAnswer(onStartPrinterStateTracking).when(callbacks).onStartPrinterStateTracking(
+                    any(PrinterId.class));
+        }
+        if (onStopPrinterStateTracking != null) {
+            doAnswer(onStopPrinterStateTracking).when(callbacks).onStopPrinterStateTracking(
+                    any(PrinterId.class));
+        }
+        if (onDestroy != null) {
+            doAnswer(onDestroy).when(callbacks).onDestroy();
+        }
+
+        return callbacks;
+    }
+
+    protected PrintServiceCallbacks createMockPrintServiceCallbacks(
+            Answer<PrinterDiscoverySessionCallbacks> onCreatePrinterDiscoverySessionCallbacks,
+            Answer<Void> onPrintJobQueued, Answer<Void> onRequestCancelPrintJob) {
+        final PrintServiceCallbacks service = mock(PrintServiceCallbacks.class);
+
+        doCallRealMethod().when(service).setService(any(PrintService.class));
+        when(service.getService()).thenCallRealMethod();
+
+        if (onCreatePrinterDiscoverySessionCallbacks != null) {
+            doAnswer(onCreatePrinterDiscoverySessionCallbacks).when(service)
+                    .onCreatePrinterDiscoverySessionCallbacks();
+        }
+        if (onPrintJobQueued != null) {
+            doAnswer(onPrintJobQueued).when(service).onPrintJobQueued(any(PrintJob.class));
+        }
+        if (onRequestCancelPrintJob != null) {
+            doAnswer(onRequestCancelPrintJob).when(service).onRequestCancelPrintJob(
+                    any(PrintJob.class));
+        }
+
+        return service;
+    }
+
+    protected final class CallCounter {
+        private final Object mLock = new Object();
+
+        private int mCallCount;
+
+        public void call() {
+            synchronized (mLock) {
+                mCallCount++;
+            }
+        }
+
+        public void waitForCount(int count, long timeoutMIllis) throws TimeoutException {
+            synchronized (mLock) {
+                final long startTimeMillis = SystemClock.uptimeMillis();
+                while (mCallCount < count) {
+                    try {
+                        final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
+                        final long remainingTimeMillis = timeoutMIllis - elapsedTimeMillis;
+                        if (remainingTimeMillis <= 0) {
+                            throw new TimeoutException();
+                        }
+                        mLock.wait(timeoutMIllis);
+                    } catch (InterruptedException ie) {
+                        /* ignore */
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/tests/tests/print/src/android/print/cts/IPrivilegedOperations.aidl b/tests/tests/print/src/android/print/cts/IPrivilegedOperations.aidl
new file mode 100644
index 0000000..93c8c3e
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/IPrivilegedOperations.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2014 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.print.cts;
+
+interface IPrivilegedOperations {
+    boolean clearApplicationUserData(String packageName);
+}
diff --git a/tests/tests/print/src/android/print/cts/PageRangeAdjustmentTest.java b/tests/tests/print/src/android/print/cts/PageRangeAdjustmentTest.java
new file mode 100644
index 0000000..d3d5a4c
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/PageRangeAdjustmentTest.java
@@ -0,0 +1,510 @@
+/*
+ * Copyright (C) 2014 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.print.cts;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
+
+import android.os.ParcelFileDescriptor;
+import android.print.PageRange;
+import android.print.PrintAttributes;
+import android.print.PrintAttributes.Margins;
+import android.print.PrintAttributes.MediaSize;
+import android.print.PrintAttributes.Resolution;
+import android.print.PrintDocumentAdapter;
+import android.print.PrintDocumentAdapter.LayoutResultCallback;
+import android.print.PrintDocumentAdapter.WriteResultCallback;
+import android.print.PrintDocumentInfo;
+import android.print.PrinterCapabilitiesInfo;
+import android.print.PrinterId;
+import android.print.PrinterInfo;
+import android.print.cts.services.FirstPrintService;
+import android.print.cts.services.PrintServiceCallbacks;
+import android.print.cts.services.PrinterDiscoverySessionCallbacks;
+import android.print.cts.services.SecondPrintService;
+import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.printservice.PrintJob;
+import android.printservice.PrintService;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.support.test.uiautomator.UiSelector;
+
+import org.mockito.InOrder;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This test verifies that the system correctly adjust the
+ * page ranges to be printed depending whether the app gave
+ * the requested pages, more pages, etc.
+ */
+public class PageRangeAdjustmentTest extends BasePrintTest {
+
+    private static final String FIRST_PRINTER = "First printer";
+
+    public void testAllPagesWantedAndAllPagesWritten() throws Exception {
+        // Create a callback for the target print service.
+        PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
+            new Answer<PrinterDiscoverySessionCallbacks>() {
+            @Override
+            public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
+                    return createMockFirstPrinterDiscoverySessionCallbacks();
+                }
+            },
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) {
+                PrintJob printJob = (PrintJob) invocation.getArguments()[0];
+                PageRange[] pages = printJob.getInfo().getPages();
+                assert(pages.length == 1 && PageRange.ALL_PAGES.equals(pages[0]));
+                printJob.complete();
+                onPrintJobQueuedCalled();
+                return null;
+            }
+        }, null);
+
+        // Configure the print services.
+        FirstPrintService.setCallbacks(firstServiceCallbacks);
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+                        .setPageCount(3)
+                        .build();
+                callback.onLayoutFinished(info, false);
+                // Mark layout was called.
+                onLayoutCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                PageRange[] pages = (PageRange[]) args[0];
+                ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                WriteResultCallback callback = (WriteResultCallback) args[3];
+                fd.close();
+                callback.onWriteFinished(pages);
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write.
+        waitForWriteForAdapterCallback();
+
+        // Select the first printer.
+        selectPrinter(FIRST_PRINTER);
+
+        // Wait for layout as the printer has different capabilities.
+        waitForLayoutAdapterCallbackCount(2);
+
+        // Click the print button.
+        clickPrintButton();
+
+        // Wait for finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Wait for the print job.
+        waitForServiceOnPrintJobQueuedCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(firstServiceCallbacks);
+
+        // We create a new session first.
+        inOrder.verify(firstServiceCallbacks)
+                .onCreatePrinterDiscoverySessionCallbacks();
+
+        // Next we wait for a call with the print job.
+        inOrder.verify(firstServiceCallbacks).onPrintJobQueued(
+                any(PrintJob.class));
+    }
+
+    public void testSomePagesWantedAndAllPagesWritten() throws Exception {
+        // Create a callback for the target print service.
+        PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
+            new Answer<PrinterDiscoverySessionCallbacks>() {
+            @Override
+            public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
+                    return createMockFirstPrinterDiscoverySessionCallbacks();
+                }
+            },
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) {
+                PrintJob printJob = (PrintJob) invocation.getArguments()[0];
+                PageRange[] pages = printJob.getInfo().getPages();
+                // We always as for the first page for preview and in this
+                // case we write all, i.e. more that needed.
+                assertTrue(pages.length == 1 && pages[0].getStart() == 1
+                        && pages[0].getEnd() == 1);
+                printJob.complete();
+                onPrintJobQueuedCalled();
+                return null;
+            }
+        }, null);
+
+        // Configure the print services.
+        FirstPrintService.setCallbacks(firstServiceCallbacks);
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+                        .setPageCount(3)
+                        .build();
+                callback.onLayoutFinished(info, false);
+                // Mark layout was called.
+                onLayoutCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                WriteResultCallback callback = (WriteResultCallback) args[3];
+                fd.close();
+                callback.onWriteFinished(new PageRange[] {PageRange.ALL_PAGES});
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write.
+        waitForWriteForAdapterCallback();
+
+        // Select the first printer.
+        selectPrinter(FIRST_PRINTER);
+
+        // Wait for layout as the printer has different capabilities.
+        waitForLayoutAdapterCallbackCount(2);
+
+        // Select only the second page.
+        selectPages("2");
+
+        // Click the print button.
+        clickPrintButton();
+
+        // Wait for finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Wait for the print job.
+        waitForServiceOnPrintJobQueuedCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(firstServiceCallbacks);
+
+        // We create a new session first.
+        inOrder.verify(firstServiceCallbacks)
+                .onCreatePrinterDiscoverySessionCallbacks();
+
+        // Next we wait for a call with the print job.
+        inOrder.verify(firstServiceCallbacks).onPrintJobQueued(
+                any(PrintJob.class));
+    }
+
+    public void testSomePagesWantedAndSomeMorePagesWritten() throws Exception {
+        // Create a callback for the target print service.
+        PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
+            new Answer<PrinterDiscoverySessionCallbacks>() {
+            @Override
+            public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
+                    return createMockFirstPrinterDiscoverySessionCallbacks();
+                }
+            },
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) {
+                PrintJob printJob = (PrintJob) invocation.getArguments()[0];
+                PageRange[] pages = printJob.getInfo().getPages();
+                assert(pages.length == 1 && pages[0].getStart() == 1
+                        && pages[0].getEnd() == 2);
+                printJob.complete();
+                onPrintJobQueuedCalled();
+                return null;
+            }
+        }, null);
+
+        // Configure the print services.
+        FirstPrintService.setCallbacks(firstServiceCallbacks);
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+                        .setPageCount(3)
+                        .build();
+                callback.onLayoutFinished(info, false);
+                // Mark layout was called.
+                onLayoutCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                PageRange[] pages = (PageRange[]) args[0];
+                ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                WriteResultCallback callback = (WriteResultCallback) args[3];
+                // We expect a single range as it is either the first page
+                // or the page we selected in the UI.
+                assertSame(pages.length, 1);
+                fd.close();
+
+                PageRange reqeustedPages = pages[0];
+                if (reqeustedPages.getStart() == reqeustedPages.getEnd()
+                        && reqeustedPages.getEnd() == 0) {
+                    // If asked for the first page, which is for preview
+                    // then write it...
+                    callback.onWriteFinished(pages);
+                } else {
+                    // otherwise write a page more that the one we selected.
+                    callback.onWriteFinished(new PageRange[] {new PageRange(2, 3)});
+                }
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write.
+        waitForWriteForAdapterCallback();
+
+        // Select the first printer.
+        selectPrinter(FIRST_PRINTER);
+
+        // Wait for layout as the printer has different capabilities.
+        waitForLayoutAdapterCallbackCount(2);
+
+        // Select only the third page.
+        selectPages("3");
+
+        // Click the print button.
+        clickPrintButton();
+
+        // Wait for finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Wait for the print job.
+        waitForServiceOnPrintJobQueuedCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(firstServiceCallbacks);
+
+        // We create a new session first.
+        inOrder.verify(firstServiceCallbacks)
+                .onCreatePrinterDiscoverySessionCallbacks();
+
+        // Next we wait for a call with the print job.
+        inOrder.verify(firstServiceCallbacks).onPrintJobQueued(
+                any(PrintJob.class));
+    }
+
+    public void testSomePagesWantedAndNotWritten() throws Exception {
+        // Create a callback for the target print service.
+        PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
+            new Answer<PrinterDiscoverySessionCallbacks>() {
+            @Override
+            public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
+                    return createMockFirstPrinterDiscoverySessionCallbacks();
+                }
+            },
+            null, null);
+
+        // Configure the print services.
+        FirstPrintService.setCallbacks(firstServiceCallbacks);
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+                        .setPageCount(3)
+                        .build();
+                callback.onLayoutFinished(info, false);
+                // Mark layout was called.
+                onLayoutCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                PageRange[] pages = (PageRange[]) args[0];
+                ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                WriteResultCallback callback = (WriteResultCallback) args[3];
+                assertSame(pages.length, 1);
+                fd.close();
+
+                // We should be asked for the first page...
+                assertSame(pages[0].getStart(), 0);
+                assertSame(pages[0].getEnd(), 0);
+
+                // ...just write a the wring page.
+                callback.onWriteFinished(new PageRange[] {new PageRange(1, 1)});
+
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write.
+        waitForWriteForAdapterCallback();
+
+        // Cancel printing.
+        UiDevice.getInstance().pressBack(); // wakes up the device.
+        UiDevice.getInstance().pressBack();
+
+        // Wait for finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(firstServiceCallbacks);
+
+        // We create a new session first.
+        inOrder.verify(firstServiceCallbacks)
+                .onCreatePrinterDiscoverySessionCallbacks();
+
+        // We should not receive a print job callback.
+        inOrder.verify(firstServiceCallbacks, never()).onPrintJobQueued(
+                any(PrintJob.class));
+    }
+
+    private void selectPages(String pages) throws UiObjectNotFoundException {
+        UiObject pagesSpinner = new UiObject(new UiSelector().resourceId(
+                "com.android.printspooler:id/range_options_spinner"));
+        pagesSpinner.click();
+
+        UiObject rangeOption = new UiObject(new UiSelector().text("Range"));
+        rangeOption.click();
+
+        UiObject pagesEditText = new UiObject(new UiSelector().resourceId(
+                "com.android.printspooler:id/page_range_edittext"));
+        pagesEditText.setText(pages);
+    }
+
+    private PrinterDiscoverySessionCallbacks createMockFirstPrinterDiscoverySessionCallbacks() {
+        return createMockPrinterDiscoverySessionCallbacks(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) {
+                PrinterDiscoverySessionCallbacks mock = (PrinterDiscoverySessionCallbacks)
+                        invocation.getMock();
+
+                StubbablePrinterDiscoverySession session = mock.getSession();
+                PrintService service = session.getService();
+
+                if (session.getPrinters().isEmpty()) {
+                          List<PrinterInfo> printers = new ArrayList<PrinterInfo>();
+
+                    // Add one printer.
+                    PrinterId firstPrinterId = service.generatePrinterId("first_printer");
+                    PrinterCapabilitiesInfo firstCapabilities =
+                            new PrinterCapabilitiesInfo.Builder(firstPrinterId)
+                        .setMinMargins(new Margins(200, 200, 200, 200))
+                        .addMediaSize(MediaSize.ISO_A4, true)
+                        .addMediaSize(MediaSize.ISO_A5, false)
+                        .addResolution(new Resolution("300x300", "300x300", 300, 300), true)
+                        .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
+                                PrintAttributes.COLOR_MODE_COLOR)
+                        .build();
+                    PrinterInfo firstPrinter = new PrinterInfo.Builder(firstPrinterId,
+                            FIRST_PRINTER, PrinterInfo.STATUS_IDLE)
+                        .setCapabilities(firstCapabilities)
+                        .build();
+                    printers.add(firstPrinter);
+
+                    session.addPrinters(printers);
+                }
+
+                return null;
+            }
+        }, null, null, null, null, null);
+    }
+
+    private PrintServiceCallbacks createSecondMockPrintServiceCallbacks() {
+        return createMockPrintServiceCallbacks(null, null, null);
+    }
+}
diff --git a/tests/tests/print/src/android/print/cts/PrintDocumentActivity.java b/tests/tests/print/src/android/print/cts/PrintDocumentActivity.java
new file mode 100644
index 0000000..6a191a6
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/PrintDocumentActivity.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 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.print.cts;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class PrintDocumentActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+}
diff --git a/tests/tests/print/src/android/print/cts/PrintDocumentAdapterContractTest.java b/tests/tests/print/src/android/print/cts/PrintDocumentAdapterContractTest.java
new file mode 100644
index 0000000..57eae3f
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/PrintDocumentAdapterContractTest.java
@@ -0,0 +1,1482 @@
+/*
+ * Copyright (C) 2014 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.print.cts;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.os.CancellationSignal;
+import android.os.CancellationSignal.OnCancelListener;
+import android.os.ParcelFileDescriptor;
+import android.print.PageRange;
+import android.print.PrintAttributes;
+import android.print.PrintAttributes.Margins;
+import android.print.PrintAttributes.MediaSize;
+import android.print.PrintAttributes.Resolution;
+import android.print.PrintDocumentAdapter;
+import android.print.PrintDocumentAdapter.LayoutResultCallback;
+import android.print.PrintDocumentAdapter.WriteResultCallback;
+import android.print.PrintDocumentInfo;
+import android.print.PrinterCapabilitiesInfo;
+import android.print.PrinterId;
+import android.print.PrinterInfo;
+import android.print.cts.services.FirstPrintService;
+import android.print.cts.services.PrintServiceCallbacks;
+import android.print.cts.services.PrinterDiscoverySessionCallbacks;
+import android.print.cts.services.SecondPrintService;
+import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.printservice.PrintJob;
+import android.printservice.PrintService;
+import android.support.test.uiautomator.UiDevice;
+
+import org.mockito.InOrder;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This test verifies that the system respects the {@link PrintDocumentAdapter}
+ * contract and invokes all callbacks as expected.
+ */
+public class PrintDocumentAdapterContractTest extends BasePrintTest {
+
+    public void testNoPrintOptionsOrPrinterChange() throws Exception {
+        // Configure the print services.
+        FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).setPageCount(1)
+                        .build();
+                callback.onLayoutFinished(info, false);
+                // Mark layout was called.
+                onLayoutCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                PageRange[] pages = (PageRange[]) args[0];
+                ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                WriteResultCallback callback = (WriteResultCallback) args[3];
+                fd.close();
+                callback.onWriteFinished(pages);
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write.
+        waitForWriteForAdapterCallback();
+
+        // Select the second printer.
+        selectPrinter("Second printer");
+
+        // Wait for layout.
+        waitForLayoutAdapterCallbackCount(2);
+
+        // Click the print button.
+        clickPrintButton();
+
+        // Wait for finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(adapter);
+
+        // Start is always called first.
+        inOrder.verify(adapter).onStart();
+
+        // Start is always followed by a layout. The PDF printer is selected if
+        // there are other printers but none of them was used.
+        PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
+        PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.NA_LETTER)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS)
+                .setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, firstOldAttributes, firstNewAttributes, true);
+
+        // We always ask for the first page for preview.
+        PageRange[] firstPages = new PageRange[] {new PageRange(0, 0)};
+        inOrder.verify(adapter).onWrite(eq(firstPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // We selected the second printer which does not support the media
+        // size that was selected, so a new layout happens as the size changed.
+        // Since we passed false to the layout callback meaning that the content
+        // didn't change, there shouldn't be a next call to write.
+        PrintAttributes secondOldAttributes = firstNewAttributes;
+        PrintAttributes secondNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.ISO_A3)
+                .setResolution(new Resolution("300x300", "300x300", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS)
+                .setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, secondOldAttributes, secondNewAttributes, true);
+
+        // When print is pressed we ask for a layout which is *not* for preview.
+        verifyLayoutCall(inOrder, adapter, secondNewAttributes, secondNewAttributes, false);
+
+        // When print is pressed we ask for all selected pages.
+        PageRange[] secondPages = new PageRange[] {PageRange.ALL_PAGES};
+        inOrder.verify(adapter).onWrite(eq(secondPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // Finish is always called last.
+        inOrder.verify(adapter).onFinish();
+
+        // No other call are expected.
+        verifyNoMoreInteractions(adapter);
+    }
+
+    public void testNoPrintOptionsOrPrinterChangeCanceled() throws Exception {
+        // Configure the print services.
+        FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback)
+                        invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                    .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+                    .setPageCount(1)
+                    .build();
+                callback.onLayoutFinished(info, false);
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                PageRange[] pages = (PageRange[]) args[0];
+                ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                WriteResultCallback callback = (WriteResultCallback) args[3];
+                fd.close();
+                callback.onWriteFinished(pages);
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write.
+        waitForWriteForAdapterCallback();
+
+        // Cancel the printing.
+        UiDevice.getInstance().pressBack(); // wakes up the device.
+        UiDevice.getInstance().pressBack();
+
+        // Wait for finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(adapter);
+
+        // Start is always called first.
+        inOrder.verify(adapter).onStart();
+
+        // Start is always followed by a layout. The PDF printer is selected if
+        // there are other printers but none of them was used.
+        PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
+        PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.NA_LETTER)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS)
+                .setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, firstOldAttributes, firstNewAttributes, true);
+
+        // We always ask for the first page for preview.
+        PageRange[] firstPages = new PageRange[] {new PageRange(0, 0)};
+        inOrder.verify(adapter).onWrite(eq(firstPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // Finish is always called last.
+        inOrder.verify(adapter).onFinish();
+
+        // No other call are expected.
+        verifyNoMoreInteractions(adapter);
+    }
+
+    public void testPrintOptionsChangeAndNoPrinterChange() throws Exception {
+        // Configure the print services.
+        FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback)
+                        invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                    .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+                    .setPageCount(1)
+                    .build();
+                callback.onLayoutFinished(info, false);
+                // Mark layout was called.
+                onLayoutCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                PageRange[] pages = (PageRange[]) args[0];
+                ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                WriteResultCallback callback = (WriteResultCallback) args[3];
+                fd.close();
+                callback.onWriteFinished(pages);
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write.
+        waitForWriteForAdapterCallback();
+
+        // Select the second printer.
+        selectPrinter("Second printer");
+
+        // Wait for layout.
+        waitForLayoutAdapterCallbackCount(2);
+
+        // Change the orientation.
+        changeOrientation("Landscape");
+
+        // Wait for layout.
+        waitForLayoutAdapterCallbackCount(3);
+
+        // Change the media size.
+        changeMediaSize("ISO A4");
+
+        // Wait for layout.
+        waitForLayoutAdapterCallbackCount(4);
+
+        // Change the color.
+        changeColor("Black & White");
+
+        // Wait for layout.
+        waitForLayoutAdapterCallbackCount(5);
+
+        // Click the print button.
+        clickPrintButton();
+
+        // Wait for a finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(adapter);
+
+        // Start is always called first.
+        inOrder.verify(adapter).onStart();
+
+        // Start is always followed by a layout. The PDF printer is selected if
+        // there are other printers but none of them was used.
+        PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
+        PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.NA_LETTER)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS)
+                .setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, firstOldAttributes, firstNewAttributes, true);
+
+        // We always ask for the first page for preview.
+        PageRange[] firstPages = new PageRange[] {new PageRange(0, 0)};
+        inOrder.verify(adapter).onWrite(eq(firstPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // We selected the second printer which does not support the media
+        // size that was selected, so a new layout happens as the size changed.
+        // Since we passed false to the layout callback meaning that the content
+        // didn't change, there shouldn't be a next call to write.
+        PrintAttributes secondOldAttributes = firstNewAttributes;
+        PrintAttributes secondNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.ISO_A3)
+                .setResolution(new Resolution("300x300", "300x300", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS)
+                .setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, secondOldAttributes, secondNewAttributes, true);
+
+        // We changed the orientation which triggers a layout. Since we passed
+        // false to the layout callback meaning that the content didn't change,
+        // there shouldn't be a next call to write.
+        PrintAttributes thirdOldAttributes = secondNewAttributes;
+        PrintAttributes thirdNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.ISO_A3.asLandscape())
+                .setResolution(new Resolution("300x300", "300x300", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS)
+                .setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, thirdOldAttributes, thirdNewAttributes, true);
+
+        // We changed the media size which triggers a layout. Since we passed
+        // false to the layout callback meaning that the content didn't change,
+        // there shouldn't be a next call to write.
+        PrintAttributes fourthOldAttributes = thirdNewAttributes;
+        PrintAttributes fourthNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.ISO_A4.asLandscape())
+                .setResolution(new Resolution("300x300", "300x300", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS)
+                .setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, fourthOldAttributes, fourthNewAttributes, true);
+
+        // We changed the color which triggers a layout. Since we passed
+        // false to the layout callback meaning that the content didn't change,
+        // there shouldn't be a next call to write.
+        PrintAttributes fifthOldAttributes = fourthNewAttributes;
+        PrintAttributes fifthNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.ISO_A4.asLandscape())
+                .setResolution(new Resolution("300x300", "300x300", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS)
+                .setColorMode(PrintAttributes.COLOR_MODE_MONOCHROME)
+                .build();
+        verifyLayoutCall(inOrder, adapter, fifthOldAttributes, fifthNewAttributes, true);
+
+        // When print is pressed we ask for a layout which is *not* for preview.
+        verifyLayoutCall(inOrder, adapter, fifthNewAttributes, fifthNewAttributes, false);
+
+        // When print is pressed we ask for all selected pages.
+        PageRange[] secondPages = new PageRange[] {PageRange.ALL_PAGES};
+        inOrder.verify(adapter).onWrite(eq(secondPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // Finish is always called last.
+        inOrder.verify(adapter).onFinish();
+
+        // No other call are expected.
+        verifyNoMoreInteractions(adapter);
+    }
+
+    public void testPrintOptionsChangeAndPrinterChange() throws Exception {
+        // Configure the print services.
+        FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback)
+                        invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                    .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+                    .setPageCount(1)
+                    .build();
+                callback.onLayoutFinished(info, false);
+                // Mark layout was called.
+                onLayoutCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                PageRange[] pages = (PageRange[]) args[0];
+                ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                WriteResultCallback callback = (WriteResultCallback) args[3];
+                fd.close();
+                callback.onWriteFinished(pages);
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write.
+        waitForWriteForAdapterCallback();
+
+        // Select the second printer.
+        selectPrinter("Second printer");
+
+        // Wait for layout.
+        waitForLayoutAdapterCallbackCount(2);
+
+        // Change the color.
+        changeColor("Black & White");
+
+        // Wait for layout.
+        waitForLayoutAdapterCallbackCount(3);
+
+        // Change the printer to one which supports the current media size.
+        // Select the second printer.
+        selectPrinter("First printer");
+
+        // Wait for layout.
+        waitForLayoutAdapterCallbackCount(4);
+
+        // Click the print button.
+        clickPrintButton();
+
+        // Wait for a finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(adapter);
+
+        // Start is always called first.
+        inOrder.verify(adapter).onStart();
+
+        // Start is always followed by a layout. The PDF printer is selected if
+        // there are other printers but none of them was used.
+        PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
+        PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.NA_LETTER)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, firstOldAttributes, firstNewAttributes, true);
+
+        // We always ask for the first page for preview.
+        PageRange[] firstPages = new PageRange[] {new PageRange(0, 0)};
+        inOrder.verify(adapter).onWrite(eq(firstPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // We changed the printer and the new printer does not support the
+        // selected media size in which case the default media size of the
+        // printer is used resulting in a layout pass. Same for margins.
+        PrintAttributes secondOldAttributes = firstNewAttributes;
+        PrintAttributes secondNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.ISO_A3)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(new Margins(0, 0, 0, 0))
+                .setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, secondOldAttributes, secondNewAttributes, true);
+
+        // We changed the printer and the new printer does not support the
+        // current color in which case the default color for the selected
+        // printer is used resulting in a layout pass.
+        PrintAttributes thirdOldAttributes = secondNewAttributes;
+        PrintAttributes thirdNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.ISO_A3)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(new Margins(0, 0, 0, 0))
+                .setColorMode(PrintAttributes.COLOR_MODE_MONOCHROME)
+                .build();
+        verifyLayoutCall(inOrder, adapter, thirdOldAttributes, thirdNewAttributes, true);
+
+        // We changed the printer to one that does not support the current
+        // media size in which case we pick the default media size for the
+        // new printer which results in a layout pass. Same for color.
+        PrintAttributes fourthOldAttributes = thirdNewAttributes;
+        PrintAttributes fourthNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.ISO_A4)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(new Margins(200, 200, 200, 200))
+                .setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, fourthOldAttributes, fourthNewAttributes, true);
+
+        // When print is pressed we ask for a layout which is *not* for preview.
+        verifyLayoutCall(inOrder, adapter, fourthNewAttributes, fourthNewAttributes, false);
+
+        // When print is pressed we ask for all selected pages.
+        PageRange[] secondPages = new PageRange[] {PageRange.ALL_PAGES};
+        inOrder.verify(adapter).onWrite(eq(secondPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // Finish is always called last.
+        inOrder.verify(adapter).onFinish();
+
+        // No other call are expected.
+        verifyNoMoreInteractions(adapter);
+    }
+
+    public void testPrintOptionsChangeAndNoPrinterChangeAndContentChange()
+            throws Exception {
+        // Configure the print services.
+        FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).setPageCount(1)
+                        .build();
+                // The content changes after every layout.
+                callback.onLayoutFinished(info, true);
+                // Mark layout was called.
+                onLayoutCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                PageRange[] pages = (PageRange[]) args[0];
+                ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                WriteResultCallback callback = (WriteResultCallback) args[3];
+                fd.close();
+                callback.onWriteFinished(pages);
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write.
+        waitForWriteForAdapterCallback();
+
+        // Select the second printer.
+        selectPrinter("Second printer");
+
+        // Wait for layout.
+        waitForLayoutAdapterCallbackCount(2);
+
+        // Click the print button.
+        clickPrintButton();
+
+        // Wait for a finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(adapter);
+
+        // Start is always called first.
+        inOrder.verify(adapter).onStart();
+
+        // Start is always followed by a layout. The PDF printer is selected if
+        // there are other printers but none of them was used.
+        PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
+        PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.NA_LETTER)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, firstOldAttributes, firstNewAttributes, true);
+
+        // We always ask for the first page for preview.
+        PageRange[] firstPages = new PageRange[] {new PageRange(0, 0)};
+        inOrder.verify(adapter).onWrite(eq(firstPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // We selected the second printer which does not support the media
+        // size that was selected, so a new layout happens as the size changed.
+        PrintAttributes secondOldAttributes = firstNewAttributes;
+        PrintAttributes secondNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.ISO_A3)
+                .setResolution(new Resolution("300x300", "300x300", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, secondOldAttributes, secondNewAttributes, true);
+
+        // In the layout callback we reported that the content changed,
+        // so the previously written page has to be written again.
+        PageRange[] secondPages = new PageRange[] {new PageRange(0, 0)};
+        inOrder.verify(adapter).onWrite(eq(secondPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // When print is pressed we ask for a layout which is *not* for preview.
+        verifyLayoutCall(inOrder, adapter, secondNewAttributes, secondNewAttributes, false);
+
+        // When print is pressed we ask for all selected pages.
+        PageRange[] thirdPages = new PageRange[] {PageRange.ALL_PAGES};
+        inOrder.verify(adapter).onWrite(eq(thirdPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // Finish is always called last.
+        inOrder.verify(adapter).onFinish();
+
+        // No other call are expected.
+        verifyNoMoreInteractions(adapter);
+    }
+
+    public void testNewPrinterSupportsSelectedPrintOptions() throws Exception {
+        // Configure the print services.
+        FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).setPageCount(1)
+                        .build();
+                // The content changes after every layout.
+                callback.onLayoutFinished(info, false);
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                PageRange[] pages = (PageRange[]) args[0];
+                ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                WriteResultCallback callback = (WriteResultCallback) args[3];
+                fd.close();
+                callback.onWriteFinished(pages);
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write.
+        waitForWriteForAdapterCallback();
+
+        // Select the third printer.
+        selectPrinter("Third printer");
+
+        // Click the print button.
+        clickPrintButton();
+
+        // Wait for a finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(adapter);
+
+        // Start is always called first.
+        inOrder.verify(adapter).onStart();
+
+        // Start is always followed by a layout. The PDF printer is selected if
+        // there are other printers but none of them was used.
+        PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
+        PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.NA_LETTER)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, firstOldAttributes, firstNewAttributes, true);
+
+        // We always ask for the first page for preview.
+        PageRange[] firstPages = new PageRange[] {new PageRange(0, 0)};
+        inOrder.verify(adapter).onWrite(eq(firstPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // When print is pressed we ask for a layout which is *not* for preview.
+        verifyLayoutCall(inOrder, adapter, firstNewAttributes, firstNewAttributes, false);
+
+        // When print is pressed we ask for all selected pages.
+        PageRange[] thirdPages = new PageRange[] {PageRange.ALL_PAGES};
+        inOrder.verify(adapter).onWrite(eq(thirdPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // Finish is always called last.
+        inOrder.verify(adapter).onFinish();
+
+        // No other call are expected.
+        verifyNoMoreInteractions(adapter);
+    }
+
+    public void testNothingChangesAllPagesWrittenFirstTime() throws Exception {
+        // Configure the print services.
+        FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).setPageCount(3)
+                        .build();
+                callback.onLayoutFinished(info, false);
+                // Mark layout was called.
+                onLayoutCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                WriteResultCallback callback = (WriteResultCallback) args[3];
+                fd.close();
+                callback.onWriteFinished(new PageRange[] {PageRange.ALL_PAGES});
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write.
+        waitForWriteForAdapterCallback();
+
+        // Select the second printer.
+        selectPrinter("Second printer");
+
+        // Wait for layout.
+        waitForLayoutAdapterCallbackCount(2);
+
+        // Click the print button.
+        clickPrintButton();
+
+        // Wait for a finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(adapter);
+
+        // Start is always called first.
+        inOrder.verify(adapter).onStart();
+
+        // Start is always followed by a layout. The PDF printer is selected if
+        // there are other printers but none of them was used.
+        PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
+        PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.NA_LETTER)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, firstOldAttributes, firstNewAttributes, true);
+
+        // We always ask for the first page for preview.
+        PageRange[] firstPages = new PageRange[] {new PageRange(0, 0)};
+        inOrder.verify(adapter).onWrite(eq(firstPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // We selected the second printer which does not support the media
+        // size that was selected, so a new layout happens as the size changed.
+        PrintAttributes secondOldAttributes = firstNewAttributes;
+        PrintAttributes secondNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.ISO_A3)
+                .setResolution(new Resolution("300x300", "300x300", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, secondOldAttributes, secondNewAttributes, true);
+
+        // In the layout callback we reported that the content didn't change,
+        // and we wrote all pages in the write call while being asked only
+        // for the first page. Hence, all pages were written and they didn't
+        // change, therefore no subsequent write call should happen.
+
+        // When print is pressed we ask for a layout which is *not* for preview.
+        verifyLayoutCall(inOrder, adapter, secondNewAttributes, secondNewAttributes, false);
+
+        // In the layout callback we reported that the content didn't change,
+        // and we wrote all pages in the write call while being asked only
+        // for the first page. Hence, all pages were written and they didn't
+        // change, therefore no subsequent write call should happen.
+
+        // Finish is always called last.
+        inOrder.verify(adapter).onFinish();
+
+        // No other call are expected.
+        verifyNoMoreInteractions(adapter);
+    }
+
+    public void testCancelLongRunningLayout() throws Exception {
+        // Configure the print services.
+        FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                CancellationSignal cancellation = (CancellationSignal) invocation.getArguments()[2];
+                final LayoutResultCallback callback = (LayoutResultCallback) invocation
+                        .getArguments()[3];
+                cancellation.setOnCancelListener(new OnCancelListener() {
+                    @Override
+                    public void onCancel() {
+                        callback.onLayoutCancelled();
+                    }
+                });
+                onLayoutCalled();
+                return null;
+            }
+        }, null, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for layout.
+        waitForLayoutAdapterCallbackCount(1);
+
+        // Cancel printing.
+        UiDevice.getInstance().pressBack(); // wakes up the device.
+        UiDevice.getInstance().pressBack();
+
+        // Wait for a finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(adapter);
+
+        // Start is always called first.
+        inOrder.verify(adapter).onStart();
+
+        // Start is always followed by a layout. The PDF printer is selected if
+        // there are other printers but none of them was used.
+        PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
+        PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.NA_LETTER)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, firstOldAttributes, firstNewAttributes, true);
+
+        // Finish is always called last.
+        inOrder.verify(adapter).onFinish();
+
+        // No other call are expected.
+        verifyNoMoreInteractions(adapter);
+    }
+
+    public void testCancelLongRunningWrite() throws Exception {
+        // Configure the print services.
+        FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).setPageCount(1)
+                        .build();
+                callback.onLayoutFinished(info, false);
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                final ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                final CancellationSignal cancellation = (CancellationSignal) args[2];
+                final WriteResultCallback callback = (WriteResultCallback) args[3];
+                cancellation.setOnCancelListener(new OnCancelListener() {
+                    @Override
+                    public void onCancel() {
+                        try {
+                            fd.close();
+                        } catch (IOException ioe) {
+                            /* ignore */
+                        }
+                        callback.onWriteCancelled();
+                    }
+                });
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write.
+        waitForWriteForAdapterCallback();
+
+        // Cancel printing.
+        UiDevice.getInstance().pressBack(); // wakes up the device.
+        UiDevice.getInstance().pressBack();
+
+        // Wait for a finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(adapter);
+
+        // Start is always called first.
+        inOrder.verify(adapter).onStart();
+
+        // Start is always followed by a layout. The PDF printer is selected if
+        // there are other printers but none of them was used.
+        PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
+        PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.NA_LETTER)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, firstOldAttributes, firstNewAttributes, true);
+
+        // We always ask for the first page for preview.
+        PageRange[] firstPages = new PageRange[] {new PageRange(0, 0)};
+        inOrder.verify(adapter).onWrite(eq(firstPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // Finish is always called last.
+        inOrder.verify(adapter).onFinish();
+
+        // No other call are expected.
+        verifyNoMoreInteractions(adapter);
+    }
+
+    public void testFailedLayout() throws Exception {
+        // Configure the print services.
+        FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                callback.onLayoutFailed(null);
+                // Mark layout was called.
+                onLayoutCalled();
+                return null;
+            }
+        }, null, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for layout.
+        waitForLayoutAdapterCallbackCount(1);
+
+        // Cancel printing.
+        UiDevice.getInstance().pressBack(); // wakes up the device.
+        UiDevice.getInstance().pressBack();
+
+        // Wait for a finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(adapter);
+
+        // Start is always called first.
+        inOrder.verify(adapter).onStart();
+
+        // Start is always followed by a layout. The PDF printer is selected if
+        // there are other printers but none of them was used.
+        PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
+        PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.NA_LETTER)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, firstOldAttributes, firstNewAttributes, true);
+
+        // No write as layout failed.
+
+        // Finish is always called last.
+        inOrder.verify(adapter).onFinish();
+
+        // No other call are expected.
+        verifyNoMoreInteractions(adapter);
+    }
+
+    public void testFailedWrite() throws Exception {
+        // Configure the print services.
+        FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).setPageCount(1)
+                        .build();
+                callback.onLayoutFinished(info, false);
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                WriteResultCallback callback = (WriteResultCallback) args[3];
+                fd.close();
+                callback.onWriteFailed(null);
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write.
+        waitForWriteForAdapterCallback();
+
+        // Cancel printing.
+        UiDevice.getInstance().pressBack(); // wakes up the device.
+        UiDevice.getInstance().pressBack();
+
+        // Wait for a finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(adapter);
+
+        // Start is always called first.
+        inOrder.verify(adapter).onStart();
+
+        // Start is always followed by a layout. The PDF printer is selected if
+        // there are other printers but none of them was used.
+        PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
+        PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.NA_LETTER)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, firstOldAttributes, firstNewAttributes, true);
+
+        // We always ask for the first page for preview.
+        PageRange[] firstPages = new PageRange[] {new PageRange(0, 0)};
+        inOrder.verify(adapter).onWrite(eq(firstPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // Finish is always called last.
+        inOrder.verify(adapter).onFinish();
+
+        // No other call are expected.
+        verifyNoMoreInteractions(adapter);
+    }
+
+    public void testRequestedPagesNotWritten() throws Exception {
+        // Configure the print services.
+        FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                      .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).setPageCount(1)
+                      .build();
+                callback.onLayoutFinished(info, false);
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                WriteResultCallback callback = (WriteResultCallback) args[3];
+                fd.close();
+                // Write wrong pages.
+                callback.onWriteFinished(new PageRange[] {
+                        new PageRange(Integer.MAX_VALUE,Integer.MAX_VALUE)});
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write.
+        waitForWriteForAdapterCallback();
+
+        // Cancel printing.
+        UiDevice.getInstance().pressBack(); // wakes up the device.
+        UiDevice.getInstance().pressBack();
+
+        // Wait for a finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(adapter);
+
+        // Start is always called first.
+        inOrder.verify(adapter).onStart();
+
+        // Start is always followed by a layout. The PDF printer is selected if
+        // there are other printers but none of them was used.
+        PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
+        PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.NA_LETTER)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, firstOldAttributes, firstNewAttributes, true);
+
+        // We always ask for the first page for preview.
+        PageRange[] firstPages = new PageRange[] {new PageRange(0, 0)};
+        inOrder.verify(adapter).onWrite(eq(firstPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // Finish is always called last.
+        inOrder.verify(adapter).onFinish();
+
+        // No other call are expected.
+        verifyNoMoreInteractions(adapter);
+    }
+
+    public void testLayoutCallbackNotCalled() throws Exception {
+        // Configure the print services.
+        FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Break the contract and never call the callback.
+                // Mark layout called.
+                onLayoutCalled();
+                return null;
+            }
+        }, null, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for layout.
+        waitForLayoutAdapterCallbackCount(1);
+
+        // Cancel printing.
+        UiDevice.getInstance().pressBack(); // wakes up the device.
+        UiDevice.getInstance().pressBack();
+
+        // Wait for a finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(adapter);
+
+        // Start is always called first.
+        inOrder.verify(adapter).onStart();
+
+        // Start is always followed by a layout. The PDF printer is selected if
+        // there are other printers but none of them was used.
+        PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
+        PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.NA_LETTER)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, firstOldAttributes, firstNewAttributes, true);
+
+        // Finish is always called last.
+        inOrder.verify(adapter).onFinish();
+
+        // No other call are expected.
+        verifyNoMoreInteractions(adapter);
+    }
+
+    public void testWriteCallbackNotCalled() throws Exception {
+        // Configure the print services.
+        FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).setPageCount(1)
+                        .build();
+                callback.onLayoutFinished(info, false);
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                fd.close();
+                // Break the contract and never call the callback.
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write.
+        waitForWriteForAdapterCallback();
+
+        // Cancel printing.
+        UiDevice.getInstance().pressBack(); // wakes up the device.
+        UiDevice.getInstance().pressBack();
+
+        // Wait for a finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(adapter);
+
+        // Start is always called first.
+        inOrder.verify(adapter).onStart();
+
+        // Start is always followed by a layout. The PDF printer is selected if
+        // there are other printers but none of them was used.
+        PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
+        PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.NA_LETTER)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, firstOldAttributes, firstNewAttributes, true);
+
+        // We always ask for the first page for preview.
+        PageRange[] firstPages = new PageRange[] {new PageRange(0, 0)};
+        inOrder.verify(adapter).onWrite(eq(firstPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // Finish is always called last.
+        inOrder.verify(adapter).onFinish();
+
+        // No other call are expected.
+        verifyNoMoreInteractions(adapter);
+    }
+
+    private PrintServiceCallbacks createFirstMockPrintServiceCallbacks() {
+        final PrinterDiscoverySessionCallbacks callbacks =
+                createMockPrinterDiscoverySessionCallbacks(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) {
+                PrinterDiscoverySessionCallbacks mock = (PrinterDiscoverySessionCallbacks)
+                        invocation.getMock();
+
+                StubbablePrinterDiscoverySession session = mock.getSession();
+                PrintService service = session.getService();
+
+                if (session.getPrinters().isEmpty()) {
+                    List<PrinterInfo> printers = new ArrayList<PrinterInfo>();
+
+                    // Add the first printer.
+                    PrinterId firstPrinterId = service.generatePrinterId("first_printer");
+                    PrinterCapabilitiesInfo firstCapabilities =
+                            new PrinterCapabilitiesInfo.Builder(firstPrinterId)
+                        .setMinMargins(new Margins(200, 200, 200, 200))
+                        .addMediaSize(MediaSize.ISO_A4, true)
+                        .addMediaSize(MediaSize.ISO_A5, false)
+                        .addResolution(new Resolution("300x300", "300x300", 300, 300), true)
+                        .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
+                                PrintAttributes.COLOR_MODE_COLOR)
+                        .build();
+                    PrinterInfo firstPrinter = new PrinterInfo.Builder(firstPrinterId,
+                            "First printer", PrinterInfo.STATUS_IDLE)
+                        .setCapabilities(firstCapabilities)
+                        .build();
+                    printers.add(firstPrinter);
+
+                    // Add the second printer.
+                    PrinterId secondPrinterId = service.generatePrinterId("second_printer");
+                    PrinterCapabilitiesInfo secondCapabilities =
+                            new PrinterCapabilitiesInfo.Builder(secondPrinterId)
+                        .addMediaSize(MediaSize.ISO_A3, true)
+                        .addMediaSize(MediaSize.ISO_A4, false)
+                        .addResolution(new Resolution("200x200", "200x200", 200, 200), true)
+                        .addResolution(new Resolution("300x300", "300x300", 300, 300), false)
+                        .setColorModes(PrintAttributes.COLOR_MODE_COLOR
+                                | PrintAttributes.COLOR_MODE_MONOCHROME,
+                                PrintAttributes.COLOR_MODE_MONOCHROME)
+                        .build();
+                    PrinterInfo secondPrinter = new PrinterInfo.Builder(secondPrinterId,
+                            "Second printer", PrinterInfo.STATUS_IDLE)
+                        .setCapabilities(secondCapabilities)
+                        .build();
+                    printers.add(secondPrinter);
+
+                    // Add the third printer.
+                    PrinterId thirdPrinterId = service.generatePrinterId("third_printer");
+                    PrinterCapabilitiesInfo thirdCapabilities =
+                            new PrinterCapabilitiesInfo.Builder(thirdPrinterId)
+                        .addMediaSize(MediaSize.NA_LETTER, true)
+                        .addResolution(new Resolution("300x300", "300x300", 300, 300), true)
+                        .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
+                                PrintAttributes.COLOR_MODE_COLOR)
+                        .build();
+                    PrinterInfo thirdPrinter = new PrinterInfo.Builder(thirdPrinterId,
+                            "Third printer", PrinterInfo.STATUS_IDLE)
+                        .setCapabilities(thirdCapabilities)
+                        .build();
+                    printers.add(thirdPrinter);
+
+                    session.addPrinters(printers);
+                }
+                return null;
+            }
+        }, null, null, null, null, null);
+        return createMockPrintServiceCallbacks(new Answer<PrinterDiscoverySessionCallbacks>() {
+            @Override
+            public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
+                return callbacks;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                PrintJob printJob = (PrintJob) invocation.getArguments()[0];
+                printJob.complete();
+                return null;
+            }
+        }, null);
+    }
+
+    private PrintServiceCallbacks createSecondMockPrintServiceCallbacks() {
+        return createMockPrintServiceCallbacks(null, null, null);
+    }
+}
diff --git a/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java b/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java
new file mode 100644
index 0000000..cb6a6d1
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2014 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.print.cts;
+
+import static org.mockito.Mockito.inOrder;
+
+import android.os.ParcelFileDescriptor;
+import android.print.PageRange;
+import android.print.PrintAttributes;
+import android.print.PrintAttributes.Margins;
+import android.print.PrintAttributes.MediaSize;
+import android.print.PrintAttributes.Resolution;
+import android.print.PrintDocumentAdapter;
+import android.print.PrintDocumentAdapter.LayoutResultCallback;
+import android.print.PrintDocumentAdapter.WriteResultCallback;
+import android.print.PrintDocumentInfo;
+import android.print.PrinterCapabilitiesInfo;
+import android.print.PrinterId;
+import android.print.PrinterInfo;
+import android.print.cts.services.FirstPrintService;
+import android.print.cts.services.PrintServiceCallbacks;
+import android.print.cts.services.PrinterDiscoverySessionCallbacks;
+import android.print.cts.services.SecondPrintService;
+import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.printservice.PrintJob;
+import android.printservice.PrinterDiscoverySession;
+import android.support.test.uiautomator.UiDevice;
+
+import org.mockito.InOrder;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * This test verifies that the system respects the {@link PrinterDiscoverySession}
+ * contract is respected.
+ */
+public class PrinterDiscoverySessionLifecycleTest extends BasePrintTest {
+    private static final String FIRST_PRINTER_NAME = "First printer";
+    private static final String SECOND_PRINTER_NAME = "Second printer";
+
+    private static final String FIRST_PRINTER_LOCAL_ID= "first_printer";
+    private static final String SECOND_PRINTER_LOCAL_ID = "second_printer";
+
+    public void testNormalLifecycle() throws Exception {
+        // Create the session callbacks that we will be checking.
+        final PrinterDiscoverySessionCallbacks firstSessionCallbacks =
+                createFirstMockPrinterDiscoverySessionCallbacks();
+
+        // Create the service callbacks for the first print service.
+        PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
+                new Answer<PrinterDiscoverySessionCallbacks>() {
+                @Override
+                public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
+                        return firstSessionCallbacks;
+                    }
+                },
+                new Answer<Void>() {
+                @Override
+                public Void answer(InvocationOnMock invocation) {
+                    PrintJob printJob = (PrintJob) invocation.getArguments()[0];
+                    // We pretend the job is handled immediately.
+                    printJob.complete();
+                    return null;
+                }
+            }, null);
+
+        // Configure the print services.
+        FirstPrintService.setCallbacks(firstServiceCallbacks);
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a print adapter that respects the print contract.
+        PrintDocumentAdapter adapter = createMockPrintDocumentAdapter();
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write of the first page.
+        waitForWriteForAdapterCallback();
+
+        // Select the first printer.
+        selectPrinter(FIRST_PRINTER_NAME);
+
+        // Wait for layout as the printer has different capabilities.
+        waitForLayoutAdapterCallbackCount(2);
+
+        // Select the second printer (same capabilities as the other
+        // one so no layout should happen).
+        selectPrinter(SECOND_PRINTER_NAME);
+
+        // While the printer discovery session is still alive store the
+        // ids of printers as we want to make some assertions about them
+        // but only the print service can create printer ids which means
+        // that we need to get the created ones.
+        PrinterId firstPrinterId = getAddedPrinterIdForLocalId(firstSessionCallbacks,
+                FIRST_PRINTER_LOCAL_ID);
+        PrinterId secondPrinterId = getAddedPrinterIdForLocalId(firstSessionCallbacks,
+                SECOND_PRINTER_LOCAL_ID);
+        assertNotNull("Coundn't find printer:" + FIRST_PRINTER_LOCAL_ID, firstPrinterId);
+        assertNotNull("Coundn't find printer:" + SECOND_PRINTER_LOCAL_ID, secondPrinterId);
+
+        // Click the print button.
+        clickPrintButton();
+
+        // Wait for all print jobs to be handled after which the session destroyed.
+        waitForPrinterDiscoverySessionDestroyCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(firstSessionCallbacks);
+
+        // We start discovery as the print dialog was up.
+        List<PrinterId> emptyPrinterIdList = Collections.emptyList();
+        inOrder.verify(firstSessionCallbacks).onStartPrinterDiscovery(
+                emptyPrinterIdList);
+
+        // We selected the first printer and now it should be tracked.
+        inOrder.verify(firstSessionCallbacks).onStartPrinterStateTracking(
+                firstPrinterId);
+
+        // We selected the second printer so the first should not be tracked.
+        inOrder.verify(firstSessionCallbacks).onStopPrinterStateTracking(
+                firstPrinterId);
+
+        // We selected the second printer and now it should be tracked.
+        inOrder.verify(firstSessionCallbacks).onStartPrinterStateTracking(
+                secondPrinterId);
+
+        // The print dialog went away so we first stop the printer tracking...
+        inOrder.verify(firstSessionCallbacks).onStopPrinterStateTracking(
+                secondPrinterId);
+
+        // ... next we stop printer discovery...
+        inOrder.verify(firstSessionCallbacks).onStopPrinterDiscovery();
+
+        // ... last the session is destroyed.
+        inOrder.verify(firstSessionCallbacks).onDestroy();
+    }
+
+    public void testStartPrinterDiscoveryWithHistoricalPrinters() throws Exception {
+        // Create the session callbacks that we will be checking.
+        final PrinterDiscoverySessionCallbacks firstSessionCallbacks =
+                createFirstMockPrinterDiscoverySessionCallbacks();
+
+        // Create the service callbacks for the first print service.
+        PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
+                new Answer<PrinterDiscoverySessionCallbacks>() {
+                @Override
+                public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
+                        return firstSessionCallbacks;
+                    }
+                },
+                new Answer<Void>() {
+                @Override
+                public Void answer(InvocationOnMock invocation) {
+                    PrintJob printJob = (PrintJob) invocation.getArguments()[0];
+                    // We pretend the job is handled immediately.
+                    printJob.complete();
+                    return null;
+                }
+            }, null);
+
+        // Configure the print services.
+        FirstPrintService.setCallbacks(firstServiceCallbacks);
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a print adapter that respects the print contract.
+        PrintDocumentAdapter adapter = createMockPrintDocumentAdapter();
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write of the first page.
+        waitForWriteForAdapterCallback();
+
+        // Select the first printer.
+        selectPrinter(FIRST_PRINTER_NAME);
+
+        // Wait for a layout to finish - first layout was for the
+        // PDF printer, second for the first printer in preview mode.
+        waitForLayoutAdapterCallbackCount(2);
+
+        // While the printer discovery session is still alive store the
+        // ids of printer as we want to make some assertions about it
+        // but only the print service can create printer ids which means
+        // that we need to get the created one.
+        PrinterId firstPrinterId = getAddedPrinterIdForLocalId(
+                firstSessionCallbacks, FIRST_PRINTER_LOCAL_ID);
+
+        // Click the print button.
+        clickPrintButton();
+
+        // Wait for the print to complete.
+        waitForAdapterFinishCallbackCalled();
+
+        // Now print again as we want to confirm that the start
+        // printer discovery passes in the priority list.
+        print(adapter);
+
+        // Wait for a layout to finish - first layout was for the
+        // PDF printer, second for the first printer in preview mode,
+        // the third for the first printer in non-preview mode, and
+        // now a fourth for the PDF printer as we are printing again.
+        waitForLayoutAdapterCallbackCount(4);
+
+        // Cancel the printing.
+        UiDevice.getInstance().pressBack(); // wakes up the device.
+        UiDevice.getInstance().pressBack();
+
+        // Wait for all print jobs to be handled after which the session destroyed.
+        waitForPrinterDiscoverySessionDestroyCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(firstSessionCallbacks);
+
+        // We start discovery as the print dialog was up.
+        List<PrinterId> priorityList = new ArrayList<PrinterId>();
+        priorityList.add(firstPrinterId);
+        inOrder.verify(firstSessionCallbacks).onStartPrinterDiscovery(
+                priorityList);
+
+        // We selected the first printer and now it should be tracked.
+        inOrder.verify(firstSessionCallbacks).onStartPrinterStateTracking(
+                firstPrinterId);
+
+        // We selected the second printer so the first should not be tracked.
+        inOrder.verify(firstSessionCallbacks).onStopPrinterStateTracking(
+                firstPrinterId);
+
+        // ...next we stop printer discovery...
+        inOrder.verify(firstSessionCallbacks).onStopPrinterDiscovery();
+
+        // ...last the session is destroyed.
+        inOrder.verify(firstSessionCallbacks).onDestroy();
+    }
+
+    private PrinterId getAddedPrinterIdForLocalId(
+            final PrinterDiscoverySessionCallbacks sessionCallbacks, String printerLocalId) {
+        final List<PrinterInfo> reportedPrinters = new ArrayList<PrinterInfo>();
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                // Grab the printer ids as only the service can create such.
+                StubbablePrinterDiscoverySession session = sessionCallbacks.getSession();
+                reportedPrinters.addAll(session.getPrinters());
+            }
+        });
+
+        final int reportedPrinterCount = reportedPrinters.size();
+        for (int i = 0; i < reportedPrinterCount; i++) {
+            PrinterInfo reportedPrinter = reportedPrinters.get(i);
+            String localId = reportedPrinter.getId().getLocalId();
+            if (printerLocalId.equals(localId)) {
+                return reportedPrinter.getId();
+            }
+        }
+
+        return null;
+    }
+
+    private PrintServiceCallbacks createSecondMockPrintServiceCallbacks() {
+        return createMockPrintServiceCallbacks(null, null, null);
+    }
+
+    private PrinterDiscoverySessionCallbacks createFirstMockPrinterDiscoverySessionCallbacks() {
+        return createMockPrinterDiscoverySessionCallbacks(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) {
+                // Get the session.
+                StubbablePrinterDiscoverySession session = ((PrinterDiscoverySessionCallbacks)
+                        invocation.getMock()).getSession();
+
+                if (session.getPrinters().isEmpty()) {
+                    List<PrinterInfo> printers = new ArrayList<PrinterInfo>();
+
+                    // Add the first printer.
+                    PrinterId firstPrinterId = session.getService().generatePrinterId(
+                            FIRST_PRINTER_LOCAL_ID);
+                    PrinterInfo firstPrinter = new PrinterInfo.Builder(firstPrinterId,
+                            FIRST_PRINTER_NAME, PrinterInfo.STATUS_IDLE)
+                        .build();
+                    printers.add(firstPrinter);
+
+                    // Add the first printer.
+                    PrinterId secondPrinterId = session.getService().generatePrinterId(
+                            SECOND_PRINTER_LOCAL_ID);
+                    PrinterInfo secondPrinter = new PrinterInfo.Builder(secondPrinterId,
+                            SECOND_PRINTER_NAME, PrinterInfo.STATUS_IDLE)
+                        .build();
+                    printers.add(secondPrinter);
+
+                    session.addPrinters(printers);
+                }
+                return null;
+            }
+        }, null, null, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Get the session.
+                StubbablePrinterDiscoverySession session = ((PrinterDiscoverySessionCallbacks)
+                        invocation.getMock()).getSession();
+
+                PrinterId trackedPrinterId = (PrinterId) invocation.getArguments()[0];
+                List<PrinterInfo> reportedPrinters = session.getPrinters();
+
+                // We should be tracking a printer that we added.
+                PrinterInfo trackedPrinter = null;
+                final int reportedPrinterCount = reportedPrinters.size();
+                for (int i = 0; i < reportedPrinterCount; i++) {
+                    PrinterInfo reportedPrinter = reportedPrinters.get(i);
+                    if (reportedPrinter.getId().equals(trackedPrinterId)) {
+                        trackedPrinter = reportedPrinter;
+                        break;
+                    }
+                }
+                assertNotNull("Can track only added printers", trackedPrinter);
+
+                // If the printer does not have capabilities reported add them.
+                if (trackedPrinter.getCapabilities() == null) {
+
+                    // Add the capabilities to emulate lazy discovery.
+                    // Same for each printer is fine for what we test.
+                    PrinterCapabilitiesInfo capabilities =
+                            new PrinterCapabilitiesInfo.Builder(trackedPrinterId)
+                        .setMinMargins(new Margins(200, 200, 200, 200))
+                        .addMediaSize(MediaSize.ISO_A4, true)
+                        .addMediaSize(MediaSize.ISO_A5, false)
+                        .addResolution(new Resolution("300x300", "300x300", 300, 300), true)
+                        .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
+                                PrintAttributes.COLOR_MODE_COLOR)
+                        .build();
+                    PrinterInfo updatedPrinter = new PrinterInfo.Builder(trackedPrinter)
+                        .setCapabilities(capabilities)
+                        .build();
+
+                    // Update the printer.
+                    List<PrinterInfo> printers = new ArrayList<PrinterInfo>();
+                    printers.add(updatedPrinter);
+                    session.addPrinters(printers);
+                }
+
+                return null;
+            }
+        }, null, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Take a note onDestroy was called.
+                onPrinterDiscoverySessionDestroyCalled();
+                return null;
+            }
+        });
+    }
+
+    public PrintDocumentAdapter createMockPrintDocumentAdapter() {
+        return createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+                        .setPageCount(3)
+                        .build();
+                callback.onLayoutFinished(info, false);
+                // Mark layout was called.
+                onLayoutCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                PageRange[] pages = (PageRange[]) args[0];
+                ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                WriteResultCallback callback = (WriteResultCallback) args[3];
+                fd.close();
+                callback.onWriteFinished(pages);
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+    }
+}
diff --git a/tests/tests/print/src/android/print/cts/services/AddPrintersActivity.java b/tests/tests/print/src/android/print/cts/services/AddPrintersActivity.java
new file mode 100644
index 0000000..c72d6f9
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/services/AddPrintersActivity.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 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.print.cts.services;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class AddPrintersActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+}
diff --git a/tests/tests/print/src/android/print/cts/services/CustomPrintOptionsActivity.java b/tests/tests/print/src/android/print/cts/services/CustomPrintOptionsActivity.java
new file mode 100644
index 0000000..9d26d81
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/services/CustomPrintOptionsActivity.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 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.print.cts.services;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class CustomPrintOptionsActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+}
diff --git a/tests/tests/print/src/android/print/cts/services/FirstPrintService.java b/tests/tests/print/src/android/print/cts/services/FirstPrintService.java
new file mode 100644
index 0000000..a234de4
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/services/FirstPrintService.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 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.print.cts.services;
+
+public class FirstPrintService extends StubbablePrintService {
+
+    private static final Object sLock = new Object();
+
+    private static PrintServiceCallbacks sCallbacks;
+
+    public static void setCallbacks(PrintServiceCallbacks callbacks) {
+        synchronized (sLock) {
+            sCallbacks = callbacks;
+        }
+    }
+
+    @Override
+    protected PrintServiceCallbacks getCallbacks() {
+        synchronized (sLock) {
+            if (sCallbacks != null) {
+                sCallbacks.setService(this);
+            }
+            return sCallbacks;
+        }
+    }
+}
diff --git a/tests/tests/print/src/android/print/cts/services/PrintServiceCallbacks.java b/tests/tests/print/src/android/print/cts/services/PrintServiceCallbacks.java
new file mode 100644
index 0000000..ff0245f
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/services/PrintServiceCallbacks.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014 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.print.cts.services;
+
+import android.printservice.PrintJob;
+import android.printservice.PrintService;
+
+public abstract class PrintServiceCallbacks {
+
+    private PrintService mService;
+
+    public PrintService getService() {
+        return mService;
+    }
+
+    public void setService(PrintService service) {
+        mService = service;
+    }
+
+    public abstract PrinterDiscoverySessionCallbacks onCreatePrinterDiscoverySessionCallbacks();
+
+    public abstract void onRequestCancelPrintJob(PrintJob printJob);
+
+    public abstract void onPrintJobQueued(PrintJob printJob);
+}
diff --git a/tests/tests/print/src/android/print/cts/services/PrinterDiscoverySessionCallbacks.java b/tests/tests/print/src/android/print/cts/services/PrinterDiscoverySessionCallbacks.java
new file mode 100644
index 0000000..6b2c3a9
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/services/PrinterDiscoverySessionCallbacks.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 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.print.cts.services;
+
+import android.print.PrinterId;
+
+import java.util.List;
+
+public abstract class PrinterDiscoverySessionCallbacks {
+
+    private StubbablePrinterDiscoverySession mSession;
+
+    public void setSession(StubbablePrinterDiscoverySession session) {
+        mSession = session;
+    }
+
+    public StubbablePrinterDiscoverySession getSession() {
+        return mSession;
+    }
+
+    public abstract void onStartPrinterDiscovery(List<PrinterId> priorityList);
+
+    public abstract void onStopPrinterDiscovery();
+
+    public abstract void onValidatePrinters(List<PrinterId> printerIds);
+
+    public abstract void onStartPrinterStateTracking(PrinterId printerId);
+
+    public abstract void onStopPrinterStateTracking(PrinterId printerId);
+
+    public abstract void onDestroy();
+}
diff --git a/tests/tests/print/src/android/print/cts/services/SecondPrintService.java b/tests/tests/print/src/android/print/cts/services/SecondPrintService.java
new file mode 100644
index 0000000..1029a8e
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/services/SecondPrintService.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 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.print.cts.services;
+
+public class SecondPrintService extends StubbablePrintService {
+
+    private static final Object sLock = new Object();
+
+    private static PrintServiceCallbacks sCallbacks;
+
+    public static void setCallbacks(PrintServiceCallbacks callbacks) {
+        synchronized (sLock) {
+            sCallbacks = callbacks;
+        }
+    }
+
+    @Override
+    protected PrintServiceCallbacks getCallbacks() {
+        synchronized (sLock) {
+            if (sCallbacks != null) {
+                sCallbacks.setService(this);
+            }
+            return sCallbacks;
+        }
+    }
+}
diff --git a/tests/tests/print/src/android/print/cts/services/SettingsActivity.java b/tests/tests/print/src/android/print/cts/services/SettingsActivity.java
new file mode 100644
index 0000000..eb23574
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/services/SettingsActivity.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 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.print.cts.services;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class SettingsActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+}
diff --git a/tests/tests/print/src/android/print/cts/services/StubbablePrintService.java b/tests/tests/print/src/android/print/cts/services/StubbablePrintService.java
new file mode 100644
index 0000000..2686b41
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/services/StubbablePrintService.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 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.print.cts.services;
+
+import android.printservice.PrintJob;
+import android.printservice.PrintService;
+import android.printservice.PrinterDiscoverySession;
+
+public abstract class StubbablePrintService extends PrintService {
+
+    @Override
+    public PrinterDiscoverySession onCreatePrinterDiscoverySession() {
+        PrintServiceCallbacks callbacks = getCallbacks();
+        if (callbacks != null) {
+            return new StubbablePrinterDiscoverySession(this,
+                    getCallbacks().onCreatePrinterDiscoverySessionCallbacks());
+        }
+        return null;
+    }
+
+    @Override
+    public void onRequestCancelPrintJob(PrintJob printJob) {
+        PrintServiceCallbacks callbacks = getCallbacks();
+        if (callbacks != null) {
+            callbacks.onRequestCancelPrintJob(printJob);
+        }
+    }
+
+    @Override
+    public void onPrintJobQueued(PrintJob printJob) {
+        PrintServiceCallbacks callbacks = getCallbacks();
+        if (callbacks != null) {
+            callbacks.onPrintJobQueued(printJob);
+        }
+    }
+
+    protected abstract PrintServiceCallbacks getCallbacks();
+}
diff --git a/tests/tests/print/src/android/print/cts/services/StubbablePrinterDiscoverySession.java b/tests/tests/print/src/android/print/cts/services/StubbablePrinterDiscoverySession.java
new file mode 100644
index 0000000..fdc2713
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/services/StubbablePrinterDiscoverySession.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2014 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.print.cts.services;
+
+import android.print.PrinterId;
+import android.printservice.PrintService;
+import android.printservice.PrinterDiscoverySession;
+
+import java.util.List;
+
+public class StubbablePrinterDiscoverySession extends PrinterDiscoverySession {
+    private final PrintService mService;
+    private final PrinterDiscoverySessionCallbacks mCallbacks;
+
+    public StubbablePrinterDiscoverySession(PrintService service,
+            PrinterDiscoverySessionCallbacks callbacks) {
+        mService = service;
+        mCallbacks = callbacks;
+        if (mCallbacks != null) {
+            mCallbacks.setSession(this);
+        }
+    }
+
+    public PrintService getService() {
+        return mService;
+    }
+
+    @Override
+    public void onStartPrinterDiscovery(List<PrinterId> priorityList) {
+        if (mCallbacks != null) {
+            mCallbacks.onStartPrinterDiscovery(priorityList);
+        }
+    }
+
+    @Override
+    public void onStopPrinterDiscovery() {
+        if (mCallbacks != null) {
+            mCallbacks.onStopPrinterDiscovery();
+        }
+    }
+
+    @Override
+    public void onValidatePrinters(List<PrinterId> printerIds) {
+        if (mCallbacks != null) {
+            mCallbacks.onValidatePrinters(printerIds);
+        }
+    }
+
+    @Override
+    public void onStartPrinterStateTracking(PrinterId printerId) {
+        if (mCallbacks != null) {
+            mCallbacks.onStartPrinterStateTracking(printerId);
+        }
+    }
+
+    @Override
+    public void onStopPrinterStateTracking(PrinterId printerId) {
+        if (mCallbacks != null) {
+            mCallbacks.onStopPrinterStateTracking(printerId);
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        if (mCallbacks != null) {
+            mCallbacks.onDestroy();
+        }
+    }
+}
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_DataTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_DataTest.java
index 7cfb183..2710ccb 100644
--- a/tests/tests/provider/src/android/provider/cts/ContactsContract_DataTest.java
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_DataTest.java
@@ -26,10 +26,12 @@
 import android.net.Uri;
 import android.os.SystemClock;
 import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Callable;
 import android.provider.ContactsContract.CommonDataKinds.Contactables;
 import android.provider.ContactsContract.CommonDataKinds.Email;
 import android.provider.ContactsContract.CommonDataKinds.Organization;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.SipAddress;
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
@@ -49,6 +51,59 @@
     private ContentResolver mResolver;
     private ContactsContract_TestDataBuilder mBuilder;
 
+    private static ContentValues[] sContentValues = new ContentValues[7];
+    static {
+        ContentValues cv1 = new ContentValues();
+        cv1.put(Contacts.DISPLAY_NAME, "Hot Tamale");
+        cv1.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+        cv1.put(Email.DATA, "tamale@acme.com");
+        cv1.put(Email.TYPE, Email.TYPE_HOME);
+        sContentValues[0] = cv1;
+
+        ContentValues cv2 = new ContentValues();
+        cv2.put(Contacts.DISPLAY_NAME, "Hot Tamale");
+        cv2.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
+        cv2.put(Phone.DATA, "510-123-5769");
+        cv2.put(Phone.TYPE, Phone.TYPE_HOME);
+        sContentValues[1] = cv2;
+
+        ContentValues cv3 = new ContentValues();
+        cv3.put(Contacts.DISPLAY_NAME, "Hot Tamale");
+        cv3.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+        cv3.put(Email.DATA, "hot@google.com");
+        cv3.put(Email.TYPE, Email.TYPE_WORK);
+        sContentValues[2] = cv3;
+
+        ContentValues cv4 = new ContentValues();
+        cv4.put(Contacts.DISPLAY_NAME, "Cold Tamago");
+        cv4.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+        cv4.put(Email.DATA, "eggs@farmers.org");
+        cv4.put(Email.TYPE, Email.TYPE_HOME);
+        sContentValues[3] = cv4;
+
+        ContentValues cv5 = new ContentValues();
+        cv5.put(Contacts.DISPLAY_NAME, "John Doe");
+        cv5.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+        cv5.put(Email.DATA, "doeassociates@deer.com");
+        cv5.put(Email.TYPE, Email.TYPE_WORK);
+        sContentValues[4] = cv5;
+
+        ContentValues cv6 = new ContentValues();
+        cv6.put(Contacts.DISPLAY_NAME, "John Doe");
+        cv6.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
+        cv6.put(Phone.DATA, "518-354-1111");
+        cv6.put(Phone.TYPE, Phone.TYPE_HOME);
+        sContentValues[5] = cv6;
+
+        ContentValues cv7 = new ContentValues();
+        cv7.put(Contacts.DISPLAY_NAME, "Cold Tamago");
+        cv7.put(Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE);
+        cv7.put(SipAddress.DATA, "mysip@sipaddress.com");
+        cv7.put(SipAddress.TYPE, SipAddress.TYPE_HOME);
+        sContentValues[6] = cv7;
+
+    }
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -182,6 +237,37 @@
         assertCursorStoredValuesWithRawContactsFilter(filterUri, ids, new ContentValues[0]);
     }
 
+    /**
+     * Verifies that Callable.CONTENT_URI returns only data items that can be called (i.e.
+     * phone numbers and sip addresses)
+     */
+    public void testCallableUri_returnsCorrectDataRows() throws Exception {
+        long[] ids = setupContactablesTestData();
+        Uri uri = Callable.CONTENT_URI;
+        assertCursorStoredValuesWithRawContactsFilter(uri, ids, sContentValues[1],
+                sContentValues[5], sContentValues[6]);
+    }
+
+    public void testCallableFilterByNameOrOrganization_returnsCorrectDataRows() throws Exception {
+        long[] ids = setupContactablesTestData();
+        Uri uri = Uri.withAppendedPath(Callable.CONTENT_FILTER_URI, "doe");
+        // Only callables belonging to John Doe (name) and Cold Tamago (organization) are returned.
+        assertCursorStoredValuesWithRawContactsFilter(uri, ids, sContentValues[5],
+                sContentValues[6]);
+    }
+
+    public void testCallableFilterByNumber_returnsCorrectDataRows() throws Exception {
+        long[] ids = setupContactablesTestData();
+        Uri uri = Uri.withAppendedPath(Callable.CONTENT_FILTER_URI, "510");
+        assertCursorStoredValuesWithRawContactsFilter(uri, ids, sContentValues[1]);
+    }
+
+    public void testCallableFilterBySipAddress_returnsCorrectDataRows() throws Exception {
+        long[] ids = setupContactablesTestData();
+        Uri uri = Uri.withAppendedPath(Callable.CONTENT_FILTER_URI, "mysip");
+        assertCursorStoredValuesWithRawContactsFilter(uri, ids, sContentValues[6]);
+    }
+
     public void testDataInsert_updatesContactLastUpdatedTimestamp() {
         DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver);
         long baseTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId);
@@ -289,6 +375,13 @@
                 .with(Email.DATA, "eggs@farmers.org")
                 .with(Email.TYPE, Email.TYPE_HOME)
                 .insert();
+        rawContact2.newDataRow(SipAddress.CONTENT_ITEM_TYPE)
+                .with(SipAddress.DATA, "mysip@sipaddress.com")
+                .with(SipAddress.TYPE, SipAddress.TYPE_HOME)
+                .insert();
+        rawContact2.newDataRow(Organization.CONTENT_ITEM_TYPE)
+                .with(Organization.COMPANY, "Doe Corp")
+                .insert();
 
         TestRawContact rawContact3 = mBuilder.newRawContact()
                 .with(RawContacts.ACCOUNT_TYPE, "test_account")
@@ -313,50 +406,6 @@
 
     // Provides functionality to set up content values for the Contactables tests
     private static class ContactablesTestHelper {
-        private static ContentValues[] sContentValues = new ContentValues[6];
-        static {
-            ContentValues cv1 = new ContentValues();
-            cv1.put(Contacts.DISPLAY_NAME, "Hot Tamale");
-            cv1.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
-            cv1.put(Email.DATA, "tamale@acme.com");
-            cv1.put(Email.TYPE, Email.TYPE_HOME);
-            sContentValues[0] = cv1;
-
-            ContentValues cv2 = new ContentValues();
-            cv2.put(Contacts.DISPLAY_NAME, "Hot Tamale");
-            cv2.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
-            cv2.put(Phone.DATA, "510-123-5769");
-            cv2.put(Phone.TYPE, Phone.TYPE_HOME);
-            sContentValues[1] = cv2;
-
-            ContentValues cv3 = new ContentValues();
-            cv3.put(Contacts.DISPLAY_NAME, "Hot Tamale");
-            cv3.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
-            cv3.put(Email.DATA, "hot@google.com");
-            cv3.put(Email.TYPE, Email.TYPE_WORK);
-            sContentValues[2] = cv3;
-
-            ContentValues cv4 = new ContentValues();
-            cv4.put(Contacts.DISPLAY_NAME, "Cold Tamago");
-            cv4.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
-            cv4.put(Email.DATA, "eggs@farmers.org");
-            cv4.put(Email.TYPE, Email.TYPE_HOME);
-            sContentValues[3] = cv4;
-
-            ContentValues cv5 = new ContentValues();
-            cv5.put(Contacts.DISPLAY_NAME, "John Doe");
-            cv5.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
-            cv5.put(Email.DATA, "doeassociates@deer.com");
-            cv5.put(Email.TYPE, Email.TYPE_WORK);
-            sContentValues[4] = cv5;
-
-            ContentValues cv6 = new ContentValues();
-            cv6.put(Contacts.DISPLAY_NAME, "John Doe");
-            cv6.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
-            cv6.put(Phone.DATA, "518-354-1111");
-            cv6.put(Phone.TYPE, Phone.TYPE_HOME);
-            sContentValues[5] = cv6;
-        }
 
         /**
          * @return An arraylist of contentValues that correspond to the provided raw contacts
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_PinnedPositionsTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_PinnedPositionsTest.java
new file mode 100644
index 0000000..2a8e806
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_PinnedPositionsTest.java
@@ -0,0 +1,489 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider.cts;
+
+import static android.provider.cts.contacts.ContactUtil.newContentValues;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.AggregationExceptions;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.PinnedPositions;
+import android.provider.ContactsContract.RawContacts;
+import android.provider.cts.contacts.CommonDatabaseUtils;
+import android.provider.cts.contacts.ContactUtil;
+import android.provider.cts.contacts.DatabaseAsserts;
+import android.provider.cts.contacts.RawContactUtil;
+import android.test.AndroidTestCase;
+
+/**
+ * CTS tests for {@link android.provider.ContactsContract.PinnedPositions} API
+ */
+public class ContactsContract_PinnedPositionsTest extends AndroidTestCase {
+    private ContentResolver mResolver;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mResolver = getContext().getContentResolver();
+    }
+
+    /**
+     * Tests that the ContactsProvider automatically stars/unstars a pinned/unpinned contact if
+     * {@link PinnedPositions#STAR_WHEN_PINNING} boolean parameter is set to true, and that the
+     * values are correctly propogated to the contact's constituent raw contacts.
+     */
+    public void testPinnedPositionsUpdateForceStar() {
+        final DatabaseAsserts.ContactIdPair i1 = DatabaseAsserts.assertAndCreateContact(mResolver);
+        final DatabaseAsserts.ContactIdPair i2 = DatabaseAsserts.assertAndCreateContact(mResolver);
+        final DatabaseAsserts.ContactIdPair i3 = DatabaseAsserts.assertAndCreateContact(mResolver);
+        final DatabaseAsserts.ContactIdPair i4 = DatabaseAsserts.assertAndCreateContact(mResolver);
+
+        final int unpinned = PinnedPositions.UNPINNED;
+
+        assertValuesForContact(i1.mContactId,
+                newContentValues(Contacts.PINNED, unpinned, Contacts.STARRED, 0));
+        assertValuesForContact(i2.mContactId,
+                newContentValues(Contacts.PINNED, unpinned, Contacts.STARRED, 0));
+        assertValuesForContact(i3.mContactId,
+                newContentValues(Contacts.PINNED, unpinned, Contacts.STARRED, 0));
+        assertValuesForContact(i4.mContactId,
+                newContentValues(Contacts.PINNED, unpinned, Contacts.STARRED, 0));
+
+        assertValuesForRawContact(i1.mRawContactId, newContentValues(RawContacts.PINNED, unpinned));
+        assertValuesForRawContact(i2.mRawContactId, newContentValues(RawContacts.PINNED, unpinned));
+        assertValuesForRawContact(i3.mRawContactId, newContentValues(RawContacts.PINNED, unpinned));
+        assertValuesForRawContact(i4.mRawContactId, newContentValues(RawContacts.PINNED, unpinned));
+
+        final ContentValues values =
+                newContentValues(i1.mContactId, 1, i3.mContactId, 3, i4.mContactId, 2);
+        mResolver.update(ContactsContract.PinnedPositions.UPDATE_URI.buildUpon()
+                .appendQueryParameter(PinnedPositions.STAR_WHEN_PINNING, "true").build(),
+                values, null, null);
+
+        // Pinning a contact should automatically star it if we specified the boolean parameter.
+        assertValuesForContact(i1.mContactId,
+                newContentValues(Contacts.PINNED, 1, Contacts.STARRED, 1));
+        assertValuesForContact(i2.mContactId,
+                newContentValues(Contacts.PINNED, unpinned, Contacts.STARRED, 0));
+        assertValuesForContact(i3.mContactId,
+                newContentValues(Contacts.PINNED, 3, Contacts.STARRED, 1));
+        assertValuesForContact(i4.mContactId,
+                newContentValues(Contacts.PINNED, 2, Contacts.STARRED, 1));
+
+        // Make sure the values are propagated to raw contacts.
+        assertValuesForRawContact(i1.mRawContactId, newContentValues(RawContacts.PINNED, 1));
+        assertValuesForRawContact(i2.mRawContactId, newContentValues(RawContacts.PINNED, unpinned));
+        assertValuesForRawContact(i3.mRawContactId, newContentValues(RawContacts.PINNED, 3));
+        assertValuesForRawContact(i4.mRawContactId, newContentValues(RawContacts.PINNED, 2));
+
+        final ContentValues unpin = newContentValues(i3.mContactId, unpinned);
+        mResolver.update(ContactsContract.PinnedPositions.UPDATE_URI.buildUpon()
+                .appendQueryParameter(PinnedPositions.STAR_WHEN_PINNING, "true").build(),
+                unpin, null, null);
+
+        // Unpinning a contact should automatically unstar it.
+        assertValuesForContact(i1.mContactId,
+                newContentValues(Contacts.PINNED, 1, Contacts.STARRED, 1));
+        assertValuesForContact(i2.mContactId,
+                newContentValues(Contacts.PINNED, unpinned, Contacts.STARRED, 0));
+        assertValuesForContact(i3.mContactId,
+                newContentValues(Contacts.PINNED, unpinned, Contacts.STARRED, 0));
+        assertValuesForContact(i4.mContactId,
+                newContentValues(Contacts.PINNED, 2, Contacts.STARRED, 1));
+
+        assertValuesForRawContact(i1.mRawContactId,
+                newContentValues(RawContacts.PINNED, 1, RawContacts.STARRED, 1));
+        assertValuesForRawContact(i2.mRawContactId,
+                newContentValues(RawContacts.PINNED, unpinned, RawContacts.STARRED, 0));
+        assertValuesForRawContact(i3.mRawContactId,
+                newContentValues(RawContacts.PINNED, unpinned, RawContacts.STARRED, 0));
+        assertValuesForRawContact(i4.mRawContactId,
+                newContentValues(RawContacts.PINNED, 2, RawContacts.STARRED, 1));
+
+        ContactUtil.delete(mResolver, i1.mContactId);
+        ContactUtil.delete(mResolver, i2.mContactId);
+        ContactUtil.delete(mResolver, i3.mContactId);
+        ContactUtil.delete(mResolver, i4.mContactId);
+    }
+
+    /**
+     * Tests that the ContactsProvider does not automatically star/unstar a pinned/unpinned contact
+     * if {@link PinnedPositions#STAR_WHEN_PINNING} boolean parameter not set to true or not
+     * provided.
+     */
+    public void testPinnedPositionsUpdateDontForceStar() {
+        final DatabaseAsserts.ContactIdPair i1 = DatabaseAsserts.assertAndCreateContact(mResolver);
+        final DatabaseAsserts.ContactIdPair i2 = DatabaseAsserts.assertAndCreateContact(mResolver);
+        final DatabaseAsserts.ContactIdPair i3 = DatabaseAsserts.assertAndCreateContact(mResolver);
+        final DatabaseAsserts.ContactIdPair i4 = DatabaseAsserts.assertAndCreateContact(mResolver);
+
+        final int unpinned = PinnedPositions.UNPINNED;
+
+        final ContentValues values =
+                newContentValues(i1.mContactId, 1, i3.mContactId, 3, i4.mContactId, 2);
+        mResolver.update(ContactsContract.PinnedPositions.UPDATE_URI, values, null, null);
+
+        // Pinning a contact should not automatically star it since we didn't specify the
+        // STAR_WHEN_PINNING boolean parameter.
+        assertValuesForContact(i1.mContactId,
+                newContentValues(Contacts.PINNED, 1, Contacts.STARRED, 0));
+        assertValuesForContact(i2.mContactId,
+                newContentValues(Contacts.PINNED, unpinned, Contacts.STARRED, 0));
+        assertValuesForContact(i3.mContactId,
+                newContentValues(Contacts.PINNED, 3, Contacts.STARRED, 0));
+        assertValuesForContact(i4.mContactId,
+                newContentValues(Contacts.PINNED, 2, Contacts.STARRED, 0));
+
+        // Make sure the values are propagated to raw contacts.
+        assertValuesForRawContact(i1.mRawContactId,
+                newContentValues(RawContacts.PINNED, 1, RawContacts.STARRED, 0));
+        assertValuesForRawContact(i2.mRawContactId,
+                newContentValues(RawContacts.PINNED, unpinned, RawContacts.STARRED, 0));
+        assertValuesForRawContact(i3.mRawContactId,
+                newContentValues(RawContacts.PINNED, 3, RawContacts.STARRED, 0));
+        assertValuesForRawContact(i4.mRawContactId,
+                newContentValues(RawContacts.PINNED, 2, RawContacts.STARRED, 0));
+
+        // Manually star contact 3.
+        assertEquals(1,
+                updateItemForContact(Contacts.CONTENT_URI, i3.mContactId, Contacts.STARRED, "1"));
+
+        // Check the third contact and raw contact is starred.
+        assertValuesForContact(i1.mContactId,
+                newContentValues(Contacts.PINNED, 1, Contacts.STARRED, 0));
+        assertValuesForContact(i2.mContactId,
+                newContentValues(Contacts.PINNED, unpinned, Contacts.STARRED, 0));
+        assertValuesForContact(i3.mContactId,
+                newContentValues(Contacts.PINNED, 3, Contacts.STARRED, 1));
+        assertValuesForContact(i4.mContactId,
+                newContentValues(Contacts.PINNED, 2, Contacts.STARRED, 0));
+
+        assertValuesForRawContact(i1.mRawContactId,
+                newContentValues(RawContacts.PINNED, 1, RawContacts.STARRED, 0));
+        assertValuesForRawContact(i2.mRawContactId,
+                newContentValues(RawContacts.PINNED, unpinned, RawContacts.STARRED, 0));
+        assertValuesForRawContact(i3.mRawContactId,
+                newContentValues(RawContacts.PINNED, 3, RawContacts.STARRED, 1));
+        assertValuesForRawContact(i4.mRawContactId,
+                newContentValues(RawContacts.PINNED, 2, RawContacts.STARRED, 0));
+
+        final ContentValues unpin = newContentValues(i3.mContactId, unpinned);
+
+        mResolver.update(ContactsContract.PinnedPositions.UPDATE_URI, unpin, null, null);
+
+        // Unpinning a contact should not automatically unstar it.
+        assertValuesForContact(i1.mContactId,
+                newContentValues(Contacts.PINNED, 1, Contacts.STARRED, 0));
+        assertValuesForContact(i2.mContactId,
+                newContentValues(Contacts.PINNED, unpinned, Contacts.STARRED, 0));
+        assertValuesForContact(i3.mContactId,
+                newContentValues(Contacts.PINNED, unpinned, Contacts.STARRED, 1));
+        assertValuesForContact(i4.mContactId,
+                newContentValues(Contacts.PINNED, 2, Contacts.STARRED, 0));
+
+        assertValuesForRawContact(i1.mRawContactId,
+                newContentValues(RawContacts.PINNED, 1, RawContacts.STARRED, 0));
+        assertValuesForRawContact(i2.mRawContactId,
+                newContentValues(RawContacts.PINNED, unpinned, RawContacts.STARRED, 0));
+        assertValuesForRawContact(i3.mRawContactId,
+                newContentValues(RawContacts.PINNED, unpinned, RawContacts.STARRED, 1));
+        assertValuesForRawContact(i4.mRawContactId,
+                newContentValues(RawContacts.PINNED, 2, RawContacts.STARRED, 0));
+
+        ContactUtil.delete(mResolver, i1.mContactId);
+        ContactUtil.delete(mResolver, i2.mContactId);
+        ContactUtil.delete(mResolver, i3.mContactId);
+        ContactUtil.delete(mResolver, i4.mContactId);
+    }
+
+    /**
+     * Tests that updating the ContactsProvider with illegal pinned position correctly
+     * throws an IllegalArgumentException.
+     */
+    public void testPinnedPositionsUpdateIllegalValues() {
+        final DatabaseAsserts.ContactIdPair i1 = DatabaseAsserts.assertAndCreateContact(mResolver);
+        final DatabaseAsserts.ContactIdPair i2 = DatabaseAsserts.assertAndCreateContact(mResolver);
+        final DatabaseAsserts.ContactIdPair i3 = DatabaseAsserts.assertAndCreateContact(mResolver);
+        final DatabaseAsserts.ContactIdPair i4 = DatabaseAsserts.assertAndCreateContact(mResolver);
+
+        final int unpinned = PinnedPositions.UNPINNED;
+
+        assertValuesForContact(i1.mContactId, newContentValues(Contacts.PINNED, unpinned));
+        assertValuesForContact(i2.mContactId, newContentValues(Contacts.PINNED, unpinned));
+        assertValuesForContact(i3.mContactId, newContentValues(Contacts.PINNED, unpinned));
+        assertValuesForContact(i4.mContactId, newContentValues(Contacts.PINNED, unpinned));
+
+        // Unsupported string should throw an IllegalArgumentException.
+        final ContentValues values = newContentValues(i1.mContactId, 1, i3.mContactId, 3,
+                i4.mContactId, "undemotemeplease!");
+        try {
+            mResolver.update(ContactsContract.PinnedPositions.UPDATE_URI, values, null, null);
+            fail("Pinned position must be an integer.");
+        } catch (IllegalArgumentException expected) {
+        }
+
+        // Unsupported pinned position (e.g. float value) should throw an IllegalArgumentException.
+        final ContentValues values2 = newContentValues(i1.mContactId, "1.1");
+        try {
+            mResolver.update(ContactsContract.PinnedPositions.UPDATE_URI, values2, null, null);
+            fail("Pinned position must be an integer");
+        } catch (IllegalArgumentException expected) {
+        }
+
+        // Nothing should have been changed.
+
+        assertValuesForContact(i1.mContactId, newContentValues(Contacts.PINNED, unpinned));
+        assertValuesForContact(i2.mContactId, newContentValues(Contacts.PINNED, unpinned));
+        assertValuesForContact(i3.mContactId, newContentValues(Contacts.PINNED, unpinned));
+        assertValuesForContact(i4.mContactId, newContentValues(Contacts.PINNED, unpinned));
+
+        assertValuesForRawContact(i1.mRawContactId, newContentValues(RawContacts.PINNED, unpinned));
+        assertValuesForRawContact(i2.mRawContactId, newContentValues(RawContacts.PINNED, unpinned));
+        assertValuesForRawContact(i3.mRawContactId, newContentValues(RawContacts.PINNED, unpinned));
+        assertValuesForRawContact(i4.mRawContactId, newContentValues(RawContacts.PINNED, unpinned));
+
+        ContactUtil.delete(mResolver, i1.mContactId);
+        ContactUtil.delete(mResolver, i2.mContactId);
+        ContactUtil.delete(mResolver, i3.mContactId);
+        ContactUtil.delete(mResolver, i4.mContactId);
+    }
+
+    /**
+     * Tests that pinned positions are correctly handled after the ContactsProvider aggregates
+     * and splits raw contacts.
+     */
+    public void testPinnedPositionsAfterJoinAndSplit() {
+        final DatabaseAsserts.ContactIdPair i1 = DatabaseAsserts.assertAndCreateContact(mResolver);
+        final DatabaseAsserts.ContactIdPair i2 = DatabaseAsserts.assertAndCreateContact(mResolver);
+        final DatabaseAsserts.ContactIdPair i3 = DatabaseAsserts.assertAndCreateContact(mResolver);
+        final DatabaseAsserts.ContactIdPair i4 = DatabaseAsserts.assertAndCreateContact(mResolver);
+        final DatabaseAsserts.ContactIdPair i5 = DatabaseAsserts.assertAndCreateContact(mResolver);
+        final DatabaseAsserts.ContactIdPair i6 = DatabaseAsserts.assertAndCreateContact(mResolver);
+
+        final ContentValues values = newContentValues(i1.mContactId, 1, i2.mContactId, 2,
+                i3.mContactId, 3, i5.mContactId, 5, i6.mContactId, 6);
+        mResolver.update(ContactsContract.PinnedPositions.UPDATE_URI.buildUpon()
+                .appendQueryParameter(PinnedPositions.STAR_WHEN_PINNING, "true").build(),
+                values, null, null);
+
+        // Aggregate raw contact 1 and 4 together.
+        ContactUtil.setAggregationException(mResolver, AggregationExceptions.TYPE_KEEP_TOGETHER,
+                i1.mRawContactId, i4.mRawContactId);
+
+        // If only one contact is pinned, the resulting contact should inherit the pinned position.
+        assertValuesForContact(i1.mContactId, newContentValues(Contacts.PINNED, 1));
+        assertValuesForContact(i2.mContactId, newContentValues(Contacts.PINNED, 2));
+        assertValuesForContact(i3.mContactId, newContentValues(Contacts.PINNED, 3));
+        assertValuesForContact(i5.mContactId, newContentValues(Contacts.PINNED, 5));
+        assertValuesForContact(i6.mContactId, newContentValues(Contacts.PINNED, 6));
+
+        assertValuesForRawContact(i1.mRawContactId,
+                newContentValues(RawContacts.PINNED, 1, RawContacts.STARRED, 1));
+        assertValuesForRawContact(i2.mRawContactId,
+                newContentValues(RawContacts.PINNED, 2, RawContacts.STARRED, 1));
+        assertValuesForRawContact(i3.mRawContactId,
+                newContentValues(RawContacts.PINNED, 3, RawContacts.STARRED, 1));
+        assertValuesForRawContact(i4.mRawContactId,
+                newContentValues(RawContacts.PINNED, PinnedPositions.UNPINNED, RawContacts.STARRED,
+                        0));
+        assertValuesForRawContact(i5.mRawContactId,
+                newContentValues(RawContacts.PINNED, 5, RawContacts.STARRED, 1));
+        assertValuesForRawContact(i6.mRawContactId,
+                newContentValues(RawContacts.PINNED, 6, RawContacts.STARRED, 1));
+
+        // Aggregate raw contact 2 and 3 together.
+        ContactUtil.setAggregationException(mResolver, AggregationExceptions.TYPE_KEEP_TOGETHER,
+                i2.mRawContactId, i3.mRawContactId);
+
+        // If both raw contacts are pinned, the resulting contact should inherit the lower
+        // pinned position.
+        assertValuesForContact(i1.mContactId, newContentValues(Contacts.PINNED, 1));
+        assertValuesForContact(i2.mContactId, newContentValues(Contacts.PINNED, 2));
+        assertValuesForContact(i5.mContactId, newContentValues(Contacts.PINNED, 5));
+        assertValuesForContact(i6.mContactId, newContentValues(Contacts.PINNED, 6));
+
+        assertValuesForRawContact(i1.mRawContactId, newContentValues(RawContacts.PINNED, 1));
+        assertValuesForRawContact(i2.mRawContactId, newContentValues(RawContacts.PINNED, 2));
+        assertValuesForRawContact(i3.mRawContactId, newContentValues(RawContacts.PINNED, 3));
+        assertValuesForRawContact(i4.mRawContactId,
+                newContentValues(RawContacts.PINNED, PinnedPositions.UNPINNED));
+        assertValuesForRawContact(i5.mRawContactId, newContentValues(RawContacts.PINNED, 5));
+        assertValuesForRawContact(i6.mRawContactId, newContentValues(RawContacts.PINNED, 6));
+
+        // Split the aggregated raw contacts.
+        ContactUtil.setAggregationException(mResolver, AggregationExceptions.TYPE_KEEP_SEPARATE,
+            i1.mRawContactId, i4.mRawContactId);
+
+        // Raw contacts should be unpinned after being split, but still starred.
+        assertValuesForRawContact(i1.mRawContactId,
+                newContentValues(RawContacts.PINNED, PinnedPositions.UNPINNED, RawContacts.STARRED,
+                        1));
+        assertValuesForRawContact(i2.mRawContactId,
+                newContentValues(RawContacts.PINNED, 2, RawContacts.STARRED, 1));
+        assertValuesForRawContact(i3.mRawContactId,
+                newContentValues(RawContacts.PINNED, 3, RawContacts.STARRED, 1));
+        assertValuesForRawContact(i4.mRawContactId,
+                newContentValues(RawContacts.PINNED, PinnedPositions.UNPINNED, RawContacts.STARRED,
+                        0));
+        assertValuesForRawContact(i5.mRawContactId,
+                newContentValues(RawContacts.PINNED, 5, RawContacts.STARRED, 1));
+        assertValuesForRawContact(i6.mRawContactId,
+                newContentValues(RawContacts.PINNED, 6, RawContacts.STARRED, 1));
+
+        // Now demote contact 5.
+        final ContentValues cv = newContentValues(i5.mContactId, PinnedPositions.DEMOTED);
+        mResolver.update(ContactsContract.PinnedPositions.UPDATE_URI.buildUpon().build(),
+                cv, null, null);
+
+        // Get new contact Ids for contacts composing of raw contacts 1 and 4 because they have
+        // changed.
+        final long cId1 = RawContactUtil.queryContactIdByRawContactId(mResolver, i1.mRawContactId);
+        final long cId4 = RawContactUtil.queryContactIdByRawContactId(mResolver, i4.mRawContactId);
+
+        assertValuesForContact(cId1, newContentValues(Contacts.PINNED, PinnedPositions.UNPINNED));
+        assertValuesForContact(i2.mContactId, newContentValues(Contacts.PINNED, 2));
+        assertValuesForContact(cId4, newContentValues(Contacts.PINNED, PinnedPositions.UNPINNED));
+        assertValuesForContact(i5.mContactId,
+                newContentValues(Contacts.PINNED, PinnedPositions.DEMOTED));
+        assertValuesForContact(i6.mContactId, newContentValues(Contacts.PINNED, 6));
+
+        // Aggregate contacts 5 and 6 together.
+        ContactUtil.setAggregationException(mResolver, AggregationExceptions.TYPE_KEEP_TOGETHER,
+                i5.mRawContactId, i6.mRawContactId);
+
+        // The resulting contact should have a pinned value of 6.
+        assertValuesForContact(cId1, newContentValues(Contacts.PINNED, PinnedPositions.UNPINNED));
+        assertValuesForContact(i2.mContactId, newContentValues(Contacts.PINNED, 2));
+        assertValuesForContact(cId4, newContentValues(Contacts.PINNED, PinnedPositions.UNPINNED));
+        assertValuesForContact(i5.mContactId, newContentValues(Contacts.PINNED, 6));
+
+        ContactUtil.delete(mResolver, cId1);
+        ContactUtil.delete(mResolver, i2.mContactId);
+        ContactUtil.delete(mResolver, cId4);
+        ContactUtil.delete(mResolver, i5.mContactId);
+    }
+
+    /**
+     * Tests that pinned positions are correctly handled for contacts that have been demoted
+     * or undemoted.
+     */
+    public void testPinnedPositionsAfterDemoteAndUndemote() {
+        final DatabaseAsserts.ContactIdPair i1 = DatabaseAsserts.assertAndCreateContact(mResolver);
+        final DatabaseAsserts.ContactIdPair i2 = DatabaseAsserts.assertAndCreateContact(mResolver);
+
+        final ContentValues values =
+                newContentValues(i1.mContactId, 0, i2.mContactId, PinnedPositions.DEMOTED);
+
+        // Pin contact 1 and demote contact 2.
+        mResolver.update(ContactsContract.PinnedPositions.UPDATE_URI.buildUpon().
+                appendQueryParameter(PinnedPositions.STAR_WHEN_PINNING, "true").
+                build(), values, null, null);
+
+        assertValuesForContact(i1.mContactId,
+                newContentValues(Contacts.PINNED, 0, Contacts.STARRED, 1));
+        assertValuesForContact(i2.mContactId,
+                newContentValues(Contacts.PINNED, PinnedPositions.DEMOTED, Contacts.STARRED, 0));
+
+        assertValuesForRawContact(i1.mRawContactId,
+                newContentValues(RawContacts.PINNED, 0, RawContacts.STARRED, 1));
+        assertValuesForRawContact(i2.mRawContactId,
+                newContentValues(RawContacts.PINNED, PinnedPositions.DEMOTED, RawContacts.STARRED, 0));
+
+        // Now undemote both contacts.
+        final ContentValues values2 = newContentValues(i1.mContactId, PinnedPositions.UNDEMOTE,
+                i2.mContactId, PinnedPositions.UNDEMOTE);
+        mResolver.update(ContactsContract.PinnedPositions.UPDATE_URI.buildUpon().
+                build(), values2, null, null);
+
+        // Contact 1 remains pinned at 0, while contact 2 becomes unpinned.
+        assertValuesForContact(i1.mContactId,
+                newContentValues(Contacts.PINNED, 0, Contacts.STARRED, 1));
+        assertValuesForContact(i2.mContactId,
+                newContentValues(Contacts.PINNED, PinnedPositions.UNPINNED, Contacts.STARRED, 0));
+
+        assertValuesForRawContact(i1.mRawContactId,
+                newContentValues(RawContacts.PINNED, 0, RawContacts.STARRED, 1));
+        assertValuesForRawContact(i2.mRawContactId,
+                newContentValues(RawContacts.PINNED, PinnedPositions.UNPINNED, RawContacts.STARRED,
+                        0));
+
+        ContactUtil.delete(mResolver, i1.mContactId);
+        ContactUtil.delete(mResolver, i2.mContactId);
+    }
+
+    /**
+     * Verifies that the stored values for the contact that corresponds to the given contactId
+     * contain the exact same name-value pairs in the given ContentValues.
+     *
+     * @param contactId Id of a valid contact in the contacts database.
+     * @param contentValues A valid ContentValues object.
+     */
+    private void assertValuesForContact(long contactId, ContentValues contentValues) {
+        DatabaseAsserts.assertStoredValuesInUriMatchExactly(mResolver, Contacts.CONTENT_URI.
+                buildUpon().appendEncodedPath(String.valueOf(contactId)).build(), contentValues);
+    }
+
+    /**
+     * Verifies that the stored values for the raw contact that corresponds to the given
+     * rawContactId contain the exact same name-value pairs in the given ContentValues.
+     *
+     * @param rawContactId Id of a valid contact in the contacts database
+     * @param contentValues A valid ContentValues object
+     */
+    private void assertValuesForRawContact(long rawContactId, ContentValues contentValues) {
+        DatabaseAsserts.assertStoredValuesInUriMatchExactly(mResolver, RawContacts.CONTENT_URI.
+                buildUpon().appendEncodedPath(String.valueOf(rawContactId)).build(), contentValues);
+    }
+
+    /**
+     * Updates the contacts provider for a contact or raw contact corresponding to the given
+     * contact with key-value pairs as specified in the provided string parameters. Throws an
+     * exception if the number of provided string parameters is not zero or non-even.
+     *
+     * @param uri base URI that the provided ID will be appended onto, in order to creating the
+     * resulting URI
+     * @param id id of the contact of raw contact to perform the update for
+     * @param extras an even number of string parameters that correspond to name-value pairs
+     *
+     * @return the number of rows that were updated
+     */
+    private int updateItemForContact(Uri uri, long id, String... extras) {
+        Uri itemUri = ContentUris.withAppendedId(uri, id);
+        return updateItemForUri(itemUri, extras);
+    }
+
+    /**
+     * Updates the contacts provider for the given YRU with key-value pairs as specified in the
+     * provided string parameters. Throws an exception if the number of provided string parameters
+     * is not zero or non-even.
+     *
+     * @param uri URI to perform the update for
+     * @param extras an even number of string parameters that correspond to name-value pairs
+     *
+     * @return the number of rows that were updated
+     */
+    private int updateItemForUri(Uri uri, String... extras) {
+        ContentValues values = new ContentValues();
+        CommonDatabaseUtils.extrasVarArgsToValues(values, extras);
+        return mResolver.update(uri, values, null, null);
+    }
+}
+
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsTest.java b/tests/tests/provider/src/android/provider/cts/ContactsTest.java
index b496007..db1c4f7 100644
--- a/tests/tests/provider/src/android/provider/cts/ContactsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/ContactsTest.java
@@ -413,7 +413,10 @@
         final String[] CALLS_PROJECTION = new String[] {
                 Calls._ID, Calls.NUMBER, Calls.DATE, Calls.DURATION, Calls.TYPE,
                 Calls.NEW, Calls.CACHED_NAME, Calls.CACHED_NUMBER_TYPE,
-                Calls.CACHED_NUMBER_LABEL};
+                Calls.CACHED_NUMBER_LABEL, Calls.CACHED_FORMATTED_NUMBER,
+                Calls.CACHED_MATCHED_NUMBER, Calls.CACHED_NORMALIZED_NUMBER,
+                Calls.CACHED_LOOKUP_URI, Calls.CACHED_PHOTO_ID, Calls.COUNTRY_ISO,
+                Calls.GEOCODED_LOCATION};
         final int ID_INDEX = 0;
         final int NUMBER_INDEX = 1;
         final int DATE_INDEX = 2;
@@ -423,15 +426,30 @@
         final int CACHED_NAME_INDEX = 6;
         final int CACHED_NUMBER_TYPE_INDEX = 7;
         final int CACHED_NUMBER_LABEL_INDEX = 8;
+        final int CACHED_FORMATTED_NUMBER_INDEX = 9;
+        final int CACHED_MATCHED_NUMBER_INDEX = 10;
+        final int CACHED_NORMALIZED_NUMBER_INDEX = 11;
+        final int CACHED_LOOKUP_URI_INDEX = 12;
+        final int CACHED_PHOTO_ID_INDEX = 13;
+        final int COUNTRY_ISO_INDEX = 14;
+        final int GEOCODED_LOCATION_INDEX = 15;
 
         String insertCallsNumber = "0123456789";
         int insertCallsDuration = 120;
         String insertCallsName = "cached_name_insert";
         String insertCallsNumberLabel = "cached_label_insert";
-        String updateCallsNumber = "9876543210";
+
+        String updateCallsNumber = "987654321";
         int updateCallsDuration = 310;
         String updateCallsName = "cached_name_update";
         String updateCallsNumberLabel = "cached_label_update";
+        String updateCachedFormattedNumber = "987-654-4321";
+        String updateCachedMatchedNumber = "987-654-4321";
+        String updateCachedNormalizedNumber = "+1987654321";
+        String updateCachedLookupUri = "cached_lookup_uri_update";
+        long updateCachedPhotoId = 100;
+        String updateCountryIso = "hk";
+        String updateGeocodedLocation = "Hong Kong";
 
         try {
             // Test: insert
@@ -463,7 +481,8 @@
             int id = cursor.getInt(ID_INDEX);
             cursor.close();
 
-            // Test: update
+            // Test: update. Also add new cached fields to simulate extra cached fields being
+            // inserted into the call log after the initial lookup.
             int now = (int) new Date().getTime();
             value.clear();
             value.put(Calls.NUMBER, updateCallsNumber);
@@ -474,6 +493,13 @@
             value.put(Calls.CACHED_NAME, updateCallsName);
             value.put(Calls.CACHED_NUMBER_TYPE, Phones.TYPE_CUSTOM);
             value.put(Calls.CACHED_NUMBER_LABEL, updateCallsNumberLabel);
+            value.put(Calls.CACHED_FORMATTED_NUMBER, updateCachedFormattedNumber);
+            value.put(Calls.CACHED_MATCHED_NUMBER, updateCachedMatchedNumber);
+            value.put(Calls.CACHED_NORMALIZED_NUMBER, updateCachedNormalizedNumber);
+            value.put(Calls.CACHED_PHOTO_ID, updateCachedPhotoId);
+            value.put(Calls.COUNTRY_ISO, updateCountryIso);
+            value.put(Calls.GEOCODED_LOCATION, updateGeocodedLocation);
+            value.put(Calls.CACHED_LOOKUP_URI, updateCachedLookupUri);
 
             mCallLogProvider.update(uri, value, null, null);
             cursor = mCallLogProvider.query(Calls.CONTENT_URI, CALLS_PROJECTION,
@@ -487,6 +513,15 @@
             assertEquals(updateCallsName, cursor.getString(CACHED_NAME_INDEX));
             assertEquals(Phones.TYPE_CUSTOM, cursor.getInt(CACHED_NUMBER_TYPE_INDEX));
             assertEquals(updateCallsNumberLabel, cursor.getString(CACHED_NUMBER_LABEL_INDEX));
+            assertEquals(updateCachedFormattedNumber,
+                    cursor.getString(CACHED_FORMATTED_NUMBER_INDEX));
+            assertEquals(updateCachedMatchedNumber, cursor.getString(CACHED_MATCHED_NUMBER_INDEX));
+            assertEquals(updateCachedNormalizedNumber,
+                    cursor.getString(CACHED_NORMALIZED_NUMBER_INDEX));
+            assertEquals(updateCachedPhotoId, cursor.getLong(CACHED_PHOTO_ID_INDEX));
+            assertEquals(updateCountryIso, cursor.getString(COUNTRY_ISO_INDEX));
+            assertEquals(updateGeocodedLocation, cursor.getString(GEOCODED_LOCATION_INDEX));
+            assertEquals(updateCachedLookupUri, cursor.getString(CACHED_LOOKUP_URI_INDEX));
             cursor.close();
 
             // Test: delete
diff --git a/tests/tests/provider/src/android/provider/cts/contacts/CommonDatabaseUtils.java b/tests/tests/provider/src/android/provider/cts/contacts/CommonDatabaseUtils.java
index 833de64..d89e06c 100644
--- a/tests/tests/provider/src/android/provider/cts/contacts/CommonDatabaseUtils.java
+++ b/tests/tests/provider/src/android/provider/cts/contacts/CommonDatabaseUtils.java
@@ -16,8 +16,11 @@
 
 package android.provider.cts.contacts;
 
+import android.content.ContentValues;
 import android.database.Cursor;
 
+import junit.framework.Assert;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -67,4 +70,21 @@
             cursor.close();
         }
     }
+
+    /**
+     * Verifies that the number of string parameters is either zero or even, and inserts them
+     * into the provided ContentValues object as a set of name-value pairs. Throws an exception if
+     * the number of string parameters is odd, or a single null parameter was provided.
+     *
+     * @param values ContentValues object to insert name-value pairs into
+     * @param extras Zero or even number of string parameters
+     */
+    public static void extrasVarArgsToValues(ContentValues values, String... extras) {
+        Assert.assertNotNull(extras);
+        // Check that the number of provided string parameters is even.
+        Assert.assertEquals(0, extras.length % 2);
+        for (int i = 0; i < extras.length; i += 2) {
+            values.put(extras[i], extras[i + 1]);
+        }
+    }
 }
diff --git a/tests/tests/provider/src/android/provider/cts/contacts/ContactUtil.java b/tests/tests/provider/src/android/provider/cts/contacts/ContactUtil.java
index 2a53781..f6d67b9 100644
--- a/tests/tests/provider/src/android/provider/cts/contacts/ContactUtil.java
+++ b/tests/tests/provider/src/android/provider/cts/contacts/ContactUtil.java
@@ -22,6 +22,9 @@
 import android.database.Cursor;
 import android.net.Uri;
 import android.provider.ContactsContract;
+import android.provider.ContactsContract.AggregationExceptions;
+
+import junit.framework.Assert;
 
 /**
  * Convenience methods for operating on the Contacts table.
@@ -71,4 +74,64 @@
         }
         return CommonDatabaseUtils.NOT_FOUND;
     }
+
+    /**
+     * Verifies that the number of object parameters is either zero or even, inserts them
+     * into a new ContentValues object as a set of name-value pairs, and returns the newly created
+     * ContentValues object. Throws an exception if the number of string parameters is odd, or a
+     * single null parameter was provided.
+     *
+     * @param namesAndValues Zero or even number of object parameters to convert into name-value
+     * pairs
+     *
+     * @return newly created ContentValues containing the provided name-value pairs
+     */
+    public static ContentValues newContentValues(Object... namesAndValues) {
+        // Checks that the number of provided parameters is zero or even.
+        Assert.assertEquals(0, namesAndValues.length % 2);
+        final ContentValues contentValues = new ContentValues();
+        for (int i = 0; i < namesAndValues.length - 1; i += 2) {
+            Assert.assertNotNull(namesAndValues[i]);
+            final String name = namesAndValues[i].toString();
+            final Object value = namesAndValues[i + 1];
+            if (value == null) {
+                contentValues.putNull(name);
+            } else if (value instanceof String) {
+                contentValues.put(name, (String) value);
+            } else if (value instanceof Integer) {
+                contentValues.put(name, (Integer) value);
+            } else if (value instanceof Long) {
+                contentValues.put(name, (Long) value);
+            } else {
+                Assert.fail("Unsupported value type: " + value.getClass().getSimpleName() + " for "
+                    + " name: " + name);
+            }
+        }
+        return contentValues;
+    }
+
+    /**
+     * Updates the content resolver with two given raw contact ids and an aggregation type to
+     * manually trigger the forced aggregation, splitting of two raw contacts or specify that
+     * the provider should automatically decide whether or not to aggregate the two raw contacts.
+     *
+     * @param resolver ContentResolver from a valid context
+     * @param type One of the following aggregation exception types:
+     * {@link AggregationExceptions#TYPE_AUTOMATIC},
+     * {@link AggregationExceptions#TYPE_KEEP_SEPARATE},
+     * {@link AggregationExceptions#TYPE_KEEP_TOGETHER}
+     * @param rawContactId1 Id of the first raw contact
+     * @param rawContactId2 Id of the second raw contact
+     */
+    public static void setAggregationException(ContentResolver resolver, int type,
+        long rawContactId1, long rawContactId2) {
+        ContentValues values = new ContentValues();
+        values.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
+        values.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
+        values.put(AggregationExceptions.TYPE, type);
+        // Actually set the aggregation exception in the contacts database, and check that a
+        // single row was updated.
+        Assert.assertEquals(1, resolver.update(AggregationExceptions.CONTENT_URI, values, null,
+                  null));
+  }
 }
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/YuvTest.java b/tests/tests/renderscript/src/android/renderscript/cts/YuvTest.java
index c2c7275..21f4417 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/YuvTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/YuvTest.java
@@ -58,16 +58,10 @@
             bv[i] = (byte)r.nextInt(256);
         }
 
-        Type.Builder tb = new Type.Builder(mRS, Element.U8(mRS));
-        tb.setX(w);
-        tb.setY(h);
-        ay = Allocation.createTyped(mRS, tb.create());
-
-        tb = new Type.Builder(mRS, Element.U8(mRS));
-        tb.setX(w >> 1);
-        tb.setY(h >> 1);
-        au = Allocation.createTyped(mRS, tb.create());
-        av = Allocation.createTyped(mRS, tb.create());
+        ay = Allocation.createTyped(mRS, Type.createXY(mRS, Element.U8(mRS), w, h));
+        final Type tuv = Type.createXY(mRS, Element.U8(mRS), w >> 1, h >> 1);
+        au = Allocation.createTyped(mRS, tuv);
+        av = Allocation.createTyped(mRS, tuv);
 
         ay.copyFrom(by);
         au.copyFrom(bu);
@@ -75,11 +69,7 @@
     }
 
     public Allocation makeOutput() {
-        Type.Builder tb = new Type.Builder(mRS, Element.RGBA_8888(mRS));
-        tb.setX(width);
-        tb.setY(height);
-        Type t = tb.create();
-        return Allocation.createTyped(mRS, t);
+        return Allocation.createTyped(mRS, Type.createXY(mRS, Element.RGBA_8888(mRS), width, height));
     }
 
     // Test for the API 17 conversion path
diff --git a/tests/tests/security/jni/android_security_cts_NativeCodeTest.cpp b/tests/tests/security/jni/android_security_cts_NativeCodeTest.cpp
index acb3012..1db8e97 100644
--- a/tests/tests/security/jni/android_security_cts_NativeCodeTest.cpp
+++ b/tests/tests/security/jni/android_security_cts_NativeCodeTest.cpp
@@ -15,9 +15,13 @@
  */
 
 #include <jni.h>
+#include <linux/netlink.h>
+#include <linux/sock_diag.h>
+#include <stdio.h>
+#include <sys/socket.h>
 #include <sys/types.h>
-#include <unistd.h>
 #include <sys/syscall.h>
+#include <unistd.h>
 #include <sys/prctl.h>
 #include <sys/ptrace.h>
 #include <sys/wait.h>
@@ -25,6 +29,10 @@
 #include <stdlib.h>
 #include <cutils/log.h>
 #include <linux/perf_event.h>
+#include <errno.h>
+
+#define PASSED 0
+#define UNKNOWN_ERROR -1
 
 /*
  * Returns true iff this device is vulnerable to CVE-2013-2094.
@@ -74,6 +82,73 @@
     return true;
 }
 
+/*
+ * Will hang if vulnerable, return 0 if successful, -1 on unforseen
+ * error.
+ */
+static jint android_security_cts_NativeCodeTest_doSockDiagTest(JNIEnv* env, jobject thiz)
+{
+    int fd, nlmsg_size, err, len;
+    char buf[1024];
+    struct sockaddr_nl nladdr;
+    struct nlmsghdr *nlh;
+    struct msghdr msg;
+    struct iovec iov;
+    struct sock_diag_req* sock_diag_data;
+
+    fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG);
+    if (fd == -1) {
+        switch (errno) {
+            /* NETLINK_SOCK_DIAG not accessible, vector dne */
+            case EACCES:
+            case EAFNOSUPPORT:
+            case EPERM:
+            case EPROTONOSUPPORT:
+                return PASSED;
+            default:
+                return UNKNOWN_ERROR;
+        }
+    }
+    /* prepare and send netlink packet */
+    memset(&nladdr, 0, sizeof(nladdr));
+    nladdr.nl_family = AF_NETLINK;
+    nlmsg_size = NLMSG_ALIGN(NLMSG_HDRLEN + sizeof(sock_diag_data));
+    nlh = (nlmsghdr *)malloc(nlmsg_size);
+    nlh->nlmsg_len = nlmsg_size;
+    nlh->nlmsg_pid = 0;      //send packet to kernel
+    nlh->nlmsg_type = SOCK_DIAG_BY_FAMILY;
+    nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+    iov = { (void *) nlh, nlmsg_size };
+    msg = { (void *) &nladdr, sizeof(nladdr), &iov, 1, NULL, 0, 0 };
+    sock_diag_data = (sock_diag_req *) NLMSG_DATA(nlh);
+    sock_diag_data->sdiag_family = AF_MAX+1;
+    if ((err = sendmsg(fd, &msg, 0)) == -1) {
+        /* SELinux blocked it */
+        if (errno == 22) {
+            return PASSED;
+        } else {
+            return UNKNOWN_ERROR;
+        }
+    }
+    free(nlh);
+
+    memset(&nladdr, 0, sizeof(nladdr));
+    iov = { buf, sizeof(buf) };
+    msg = { (void *) &nladdr, sizeof(nladdr), &iov, 1, NULL, 0, 0 };
+    if ((len = recvmsg(fd, &msg, 0)) == -1) {
+        return UNKNOWN_ERROR;
+    }
+    for (nlh = (struct nlmsghdr *) buf; NLMSG_OK(nlh, len); nlh = NLMSG_NEXT (nlh, len)){
+        if (nlh->nlmsg_type == NLMSG_ERROR) {
+            /* -22 = -EINVAL from kernel */
+            if (*(int *)NLMSG_DATA(nlh) == -22) {
+                return PASSED;
+            }
+        }
+    }
+    return UNKNOWN_ERROR;
+}
+
 #define SEARCH_SIZE 0x4000
 
 static int secret;
@@ -168,6 +243,8 @@
             (void *) android_security_cts_NativeCodeTest_doPerfEventTest },
     {  "doPerfEventTest2", "()Z",
             (void *) android_security_cts_NativeCodeTest_doPerfEventTest2 },
+    {  "doSockDiagTest", "()I",
+            (void *) android_security_cts_NativeCodeTest_doSockDiagTest },
     {  "doVrootTest", "()Z",
             (void *) android_security_cts_NativeCodeTest_doVrootTest },
 };
diff --git a/tests/tests/security/src/android/security/cts/NativeCodeTest.java b/tests/tests/security/src/android/security/cts/NativeCodeTest.java
index 4781da3..30bf834 100644
--- a/tests/tests/security/src/android/security/cts/NativeCodeTest.java
+++ b/tests/tests/security/src/android/security/cts/NativeCodeTest.java
@@ -39,6 +39,12 @@
         assertTrue(doPerfEventTest2());
     }
 
+    public void testSockDiag() throws Exception {
+        int result = doSockDiagTest();
+        assertFalse("Encountered unexpected error: " + result + ".", (result == -1));
+        assertEquals(0, result);
+    }
+
     /**
      * Returns true iff this device is vulnerable to CVE-2013-2094.
      * A patch for CVE-2013-2094 can be found at
@@ -62,7 +68,13 @@
     private static native boolean doPerfEventTest2();
 
     /**
-     * ANDROID-11234878
+     * Hangs if device is vulnerable to CVE-2013-1763, returns -1 if
+     * unexpected error occurs, 0 otherwise.
+     */
+    private static native int doSockDiagTest();
+
+    /**
+     * ANDROID-11234878 / CVE-2013-6282
      *
      * Returns true if the device is patched against the vroot
      * vulnerability. Returns false if there was some problem running
diff --git a/tests/tests/security/src/android/security/cts/ServicePermissionsTest.java b/tests/tests/security/src/android/security/cts/ServicePermissionsTest.java
index 8c02d6b..7de3baa 100644
--- a/tests/tests/security/src/android/security/cts/ServicePermissionsTest.java
+++ b/tests/tests/security/src/android/security/cts/ServicePermissionsTest.java
@@ -99,7 +99,8 @@
             try {
                 serviceBinder.dump(out.getFD(), new String[0]);
             } catch (SecurityException e) {
-                if (e.getMessage().contains("android.permission.DUMP")) {
+                String msg = e.getMessage();
+                if ((msg == null) || msg.contains("android.permission.DUMP")) {
                     // Service correctly checked for DUMP permission, yay
                 } else {
                     // Service is throwing about something else; they're
diff --git a/tests/tests/telephony/src/android/telephony/cts/PhoneNumberUtilsTest.java b/tests/tests/telephony/src/android/telephony/cts/PhoneNumberUtilsTest.java
index 3fc5b28..d96743c 100644
--- a/tests/tests/telephony/src/android/telephony/cts/PhoneNumberUtilsTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/PhoneNumberUtilsTest.java
@@ -301,4 +301,29 @@
         assertTrue(PhoneNumberUtils.isWellFormedSmsAddress("+17005554141"));
         assertFalse(PhoneNumberUtils.isWellFormedSmsAddress("android"));
     }
+
+    public void testIsUriNumber() {
+        assertTrue(PhoneNumberUtils.isUriNumber("foo@google.com"));
+        assertTrue(PhoneNumberUtils.isUriNumber("xyz@zzz.org"));
+        assertFalse(PhoneNumberUtils.isUriNumber("+15103331245"));
+        assertFalse(PhoneNumberUtils.isUriNumber("+659231235"));
+    }
+
+    public void testGetUsernameFromUriNumber() {
+        assertEquals("john", PhoneNumberUtils.getUsernameFromUriNumber("john@myorg.com"));
+        assertEquals("tim_123", PhoneNumberUtils.getUsernameFromUriNumber("tim_123@zzz.org"));
+        assertEquals("5103331245", PhoneNumberUtils.getUsernameFromUriNumber("5103331245"));
+    }
+
+    public void testConvertAndStrip() {
+        // Untouched number.
+        assertEquals("123456789", PhoneNumberUtils.convertAndStrip("123456789"));
+        // Dashes should be stripped, legal separators (i.e. wild character remain untouched)
+        assertEquals("+15103331245*123", PhoneNumberUtils.convertAndStrip("+1-510-333-1245*123"));
+        // Arabic digits should be converted
+        assertEquals("5567861616", PhoneNumberUtils.convertAndStrip("٥‎٥‎٦‎٧‎٨‎٦‎١‎٦‎١‎٦‎"));
+        // Arabic digits converted and spaces stripped
+        assertEquals("5567861616", PhoneNumberUtils.convertAndStrip("٥‎ ٥‎٦‎ ٧‎ ٨‎ ٦‎ ١‎ ٦‎ ١‎ ٦‎"));
+
+    }
 }
diff --git a/tests/tests/view/src/android/view/cts/LayoutInflaterTest.java b/tests/tests/view/src/android/view/cts/LayoutInflaterTest.java
index b33a312..f5247dd 100644
--- a/tests/tests/view/src/android/view/cts/LayoutInflaterTest.java
+++ b/tests/tests/view/src/android/view/cts/LayoutInflaterTest.java
@@ -19,7 +19,6 @@
 import com.android.cts.stub.R;
 import com.android.internal.util.XmlUtils;
 
-
 import org.xmlpull.v1.XmlPullParser;
 
 import android.app.cts.MockActivity;
@@ -28,9 +27,11 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
+import android.content.res.Resources.Theme;
 import android.content.res.XmlResourceParser;
 import android.test.AndroidTestCase;
 import android.util.AttributeSet;
+import android.util.TypedValue;
 import android.util.Xml;
 import android.view.Gravity;
 import android.view.InflateException;
@@ -42,20 +43,21 @@
 import android.widget.LinearLayout;
 
 public class LayoutInflaterTest extends AndroidTestCase {
-
     private LayoutInflater mLayoutInflater;
-    private Context mContext;
-    private final Factory mFactory = new Factory() {
-        public View onCreateView(String name, Context context,
-                AttributeSet attrs) {
 
+    @SuppressWarnings("hiding")
+    private Context mContext;
+
+    private final Factory mFactory = new Factory() {
+        @Override
+        public View onCreateView(String name, Context context, AttributeSet attrs) {
             return null;
         }
     };
     private boolean isOnLoadClass;
     private final Filter mFilter = new Filter() {
-
-        @SuppressWarnings("unchecked")
+        @SuppressWarnings({ "unchecked", "rawtypes" })
+        @Override
         public boolean onLoadClass(Class clazz) {
             isOnLoadClass = true;
             return true;
@@ -148,7 +150,8 @@
         mLayoutInflater = LayoutInflater.from(mContext);
         isOnLoadClass = false;
         mLayoutInflater.setFilter(new Filter() {
-            @SuppressWarnings("unchecked")
+            @SuppressWarnings({ "unchecked", "rawtypes" })
+            @Override
             public boolean onLoadClass(Class clazz) {
                 isOnLoadClass = true;
                 return false;
@@ -307,60 +310,77 @@
     }
 
     public void testInflate4() {
-       XmlResourceParser parser = getContext().getResources().getLayout(
-               R.layout.inflater_layout);
-       View view = mLayoutInflater.inflate(parser, null, false);
-       assertNotNull(view);
-       view = null;
-       try {
-           view = mLayoutInflater.inflate(null, null, false);
-           fail("should throw exception");
-       } catch (NullPointerException e) {
-       }
-       LinearLayout mLayout;
-       mLayout = new LinearLayout(mContext);
-       mLayout.setOrientation(LinearLayout.VERTICAL);
-       mLayout.setHorizontalGravity(Gravity.LEFT);
-       mLayout.setLayoutParams(new ViewGroup.LayoutParams(
-               ViewGroup.LayoutParams.MATCH_PARENT,
-               ViewGroup.LayoutParams.MATCH_PARENT));
-       assertEquals(0, mLayout.getChildCount());
+        XmlResourceParser parser = getContext().getResources().getLayout(
+                R.layout.inflater_layout);
+        View view = mLayoutInflater.inflate(parser, null, false);
+        assertNotNull(view);
+        view = null;
+        try {
+            view = mLayoutInflater.inflate(null, null, false);
+            fail("should throw exception");
+        } catch (NullPointerException e) {
+        }
+        LinearLayout mLayout;
+        mLayout = new LinearLayout(mContext);
+        mLayout.setOrientation(LinearLayout.VERTICAL);
+        mLayout.setHorizontalGravity(Gravity.LEFT);
+        mLayout.setLayoutParams(new ViewGroup.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT));
+        assertEquals(0, mLayout.getChildCount());
 
-       try {
-           view = mLayoutInflater.inflate(parser, mLayout, false);
-           fail("should throw exception");
-       } catch (NullPointerException e) {
-       }
-       parser = getContext().getResources().getLayout(
-               R.layout.inflater_layout);
-       view = mLayoutInflater.inflate(parser, mLayout, false);
-       assertNull(view.getParent());
-       assertNotNull(view);
-       assertEquals(0, mLayout.getChildCount());
-       parser = getContext().getResources().getLayout(
-               R.layout.inflater_layout);
-       assertEquals(0, mLayout.getChildCount());
-       view = mLayoutInflater.inflate(parser, mLayout, true);
-       assertNotNull(view);
-       assertNull(view.getParent());
-       assertEquals(1, mLayout.getChildCount());
+        try {
+            view = mLayoutInflater.inflate(parser, mLayout, false);
+            fail("should throw exception");
+        } catch (NullPointerException e) {
+        }
+        parser = getContext().getResources().getLayout(
+                R.layout.inflater_layout);
+        view = mLayoutInflater.inflate(parser, mLayout, false);
+        assertNull(view.getParent());
+        assertNotNull(view);
+        assertEquals(0, mLayout.getChildCount());
+        parser = getContext().getResources().getLayout(
+                R.layout.inflater_layout);
+        assertEquals(0, mLayout.getChildCount());
+        view = mLayoutInflater.inflate(parser, mLayout, true);
+        assertNotNull(view);
+        assertNull(view.getParent());
+        assertEquals(1, mLayout.getChildCount());
 
-       parser = null;
-       parser = getParser();
-       try {
-           view = mLayoutInflater.inflate(parser, mLayout, false);
-           fail("should throw exception");
-       } catch (InflateException e) {
-       }
+        parser = null;
+        parser = getParser();
+        try {
+            view = mLayoutInflater.inflate(parser, mLayout, false);
+            fail("should throw exception");
+        } catch (InflateException e) {
+        }
 
-       parser = null;
-       view = null;
-       parser = getParser();
+        parser = null;
+        view = null;
+        parser = getParser();
 
-       view = mLayoutInflater.inflate(parser, mLayout, true);
-       assertNotNull(view);
-       assertEquals(2, mLayout.getChildCount());
-   }
+        view = mLayoutInflater.inflate(parser, mLayout, true);
+        assertNotNull(view);
+        assertEquals(2, mLayout.getChildCount());
+    }
+
+    public void testOverrideTheme() {
+        View container = mLayoutInflater.inflate(R.layout.inflater_override_theme_layout, null);
+        verifyThemeType(container, "view_outer", R.id.view_outer, 1);
+        verifyThemeType(container, "view_inner", R.id.view_inner, 2);
+        verifyThemeType(container, "view_attr", R.id.view_attr, 3);
+    }
+
+    private void verifyThemeType(View container, String tag, int id, int type) {
+        TypedValue outValue = new TypedValue();
+        View view = container.findViewById(id);
+        assertNotNull("Found " + tag, view);
+        Theme theme = view.getContext().getTheme();
+        boolean resolved = theme.resolveAttribute(R.attr.themeType, outValue, true);
+        assertTrue("Resolved themeType for " + tag, resolved);
+        assertEquals(tag + " has themeType " + type, type, outValue.data);
+    }
 
     static class MockLayoutInflater extends LayoutInflater {
 
diff --git a/tests/tests/webkit/AndroidManifest.xml b/tests/tests/webkit/AndroidManifest.xml
index 493762e..f4424a8 100644
--- a/tests/tests/webkit/AndroidManifest.xml
+++ b/tests/tests/webkit/AndroidManifest.xml
@@ -22,6 +22,9 @@
     <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
     <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>
     <application>
+        <provider android:name="android.webkit.cts.MockContentProvider"
+                  android:exported="true"
+                  android:authorities="android.webkit.cts.MockContentProvider" />
         <uses-library android:name="android.test.runner" />
     </application>
 
diff --git a/tests/tests/webkit/src/android/webkit/cts/MockContentProvider.java b/tests/tests/webkit/src/android/webkit/cts/MockContentProvider.java
new file mode 100644
index 0000000..b9f3891
--- /dev/null
+++ b/tests/tests/webkit/src/android/webkit/cts/MockContentProvider.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit.cts;
+
+import android.content.ContentProvider;
+import android.content.ContentProvider.PipeDataWriter;
+import android.content.ContentValues;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+
+public class MockContentProvider extends ContentProvider
+        implements PipeDataWriter<String> {
+
+    public static final String AUTHORITY = MockContentProvider.class.getName();
+
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        return 0;
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        return "text/html";
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues initialValues) {
+        return null;
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection,
+            String[] selectionArgs, String sortOrder) {
+        return null;
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection,
+            String[] selectionArgs) {
+        return 0;
+    }
+
+    @Override
+    public AssetFileDescriptor openAssetFile(Uri uri, String mode)
+            throws FileNotFoundException {
+        return new AssetFileDescriptor(openPipeHelper(uri, "text/html", null,
+                "<html><title>" + uri.toString() + "</title></html>", this), 0,
+                AssetFileDescriptor.UNKNOWN_LENGTH);
+    }
+
+    @Override
+    public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, String mimeType, Bundle opts,
+            String args) {
+        FileOutputStream outputStream = new FileOutputStream(output.getFileDescriptor());
+        PrintWriter printWriter = null;
+        try {
+            printWriter = new PrintWriter(new OutputStreamWriter(outputStream, "UTF-8"));
+            printWriter.print(args);
+        } catch (UnsupportedEncodingException e) {
+        } finally {
+            if (printWriter != null) {
+                printWriter.flush();
+            }
+            try {
+                outputStream.close();
+            } catch (IOException e) {
+            }
+        }
+    }
+}
+
diff --git a/tests/tests/webkit/src/android/webkit/cts/TestHtmlConstants.java b/tests/tests/webkit/src/android/webkit/cts/TestHtmlConstants.java
index 11cc1a5..cc39f48 100644
--- a/tests/tests/webkit/src/android/webkit/cts/TestHtmlConstants.java
+++ b/tests/tests/webkit/src/android/webkit/cts/TestHtmlConstants.java
@@ -65,6 +65,7 @@
     public static final String DATABASE_ACCESS_URL = "webkit/test_databaseaccess.html";
     public static final String STOP_LOADING_URL = "webkit/test_stop_loading.html";
     public static final String BLANK_TAG_URL = "webkit/blank_tag.html";
+    public static final String PAGE_WITH_LINK_URL = "webkit/page_with_link.html";
 
     // Must match the title of the page at
     // android/frameworks/base/core/res/res/raw/loaderror.html
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebChromeClientTest.java b/tests/tests/webkit/src/android/webkit/cts/WebChromeClientTest.java
index 5c9c958..344b568 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebChromeClientTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebChromeClientTest.java
@@ -112,6 +112,7 @@
         Thread.sleep(100); // Wait for open to be received on the icon db thread.
 
         assertFalse(webChromeClient.hadOnReceivedIcon());
+        assertNull(mOnUiThread.getFavicon());
 
         String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
         mOnUiThread.loadUrlAndWaitForCompletion(url);
@@ -122,6 +123,7 @@
                 return webChromeClient.hadOnReceivedIcon();
             }
         }.run();
+        assertNotNull(mOnUiThread.getFavicon());
     }
 
     public void runWindowTest(boolean expectWindowClose) throws Exception {
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
index f78c126..458d5be 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
@@ -80,11 +80,12 @@
                 "<a href=\"" + TEST_URL + "\" id=\"link\">new page</a>" +
                 "</body></html>";
         mOnUiThread.loadDataAndWaitForCompletion(data, "text/html", null);
-        clickOnLinkUsingJs("link");
+        clickOnLinkUsingJs("link", mOnUiThread);
         assertEquals(TEST_URL, webViewClient.getLastShouldOverrideUrl());
     }
 
     // Verify shouldoverrideurlloading called on webview called via onCreateWindow
+    // TODO(sgurun) upstream this test to Aw.
     public void testShouldOverrideUrlLoadingOnCreateWindow() throws Exception {
         mWebServer = new CtsTestServer(getActivity());
         // WebViewClient for main window
@@ -95,12 +96,14 @@
         mOnUiThread.getSettings().setJavaScriptEnabled(true);
         mOnUiThread.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
         mOnUiThread.getSettings().setSupportMultipleWindows(true);
+
+        final WebView childWebView = mOnUiThread.createWebView();
+
         mOnUiThread.setWebChromeClient(new WebChromeClient() {
             @Override
             public boolean onCreateWindow(
                 WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
                 WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
-                WebView childWebView = new WebView(view.getContext());
                 childWebView.setWebViewClient(childWebViewClient);
                 childWebView.getSettings().setJavaScriptEnabled(true);
                 transport.setWebView(childWebView);
@@ -119,13 +122,30 @@
                 return childWebViewClient.hasOnPageFinishedCalled();
             }
         }.run();
-        assertEquals(mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL),
+        assertEquals(mWebServer.getAssetUrl(TestHtmlConstants.PAGE_WITH_LINK_URL),
                 childWebViewClient.getLastShouldOverrideUrl());
+
+        // Now test a navigation within the page
+        //TODO(hush) Enable this portion when b/12804986 is fixed.
+        /*
+        WebViewOnUiThread childWebViewOnUiThread = new WebViewOnUiThread(this, childWebView);
+        final int childCallCount = childWebViewClient.getShouldOverrideUrlLoadingCallCount();
+        final int mainCallCount = mainWebViewClient.getShouldOverrideUrlLoadingCallCount();
+        clickOnLinkUsingJs("link", childWebViewOnUiThread);
+        new PollingCheck(TEST_TIMEOUT) {
+            @Override
+            protected boolean check() {
+                return childWebViewClient.getShouldOverrideUrlLoadingCallCount() > childCallCount;
+            }
+        }.run();
+        assertEquals(mainCallCount, mainWebViewClient.getShouldOverrideUrlLoadingCallCount());
+        assertEquals(TEST_URL, childWebViewClient.getLastShouldOverrideUrl());
+        */
     }
 
-    private void clickOnLinkUsingJs(final String linkId) {
+    private void clickOnLinkUsingJs(final String linkId, WebViewOnUiThread webViewOnUiThread) {
         EvaluateJsResultPollingCheck jsResult = new EvaluateJsResultPollingCheck("null");
-        mOnUiThread.evaluateJavascript(
+        webViewOnUiThread.evaluateJavascript(
                 "document.getElementById('" + linkId + "').click();" +
                 "console.log('element with id [" + linkId + "] clicked');", jsResult);
         jsResult.run();
@@ -287,6 +307,7 @@
         private boolean mOnReceivedHttpAuthRequestCalled;
         private boolean mOnUnhandledKeyEventCalled;
         private boolean mOnScaleChangedCalled;
+        private int mShouldOverrideUrlLoadingCallCount;
         private String mLastShouldOverrideUrl;
 
         public MockWebViewClient() {
@@ -329,6 +350,10 @@
             return mOnScaleChangedCalled;
         }
 
+        public int getShouldOverrideUrlLoadingCallCount() {
+            return mShouldOverrideUrlLoadingCallCount;
+        }
+
         public String getLastShouldOverrideUrl() {
             return mLastShouldOverrideUrl;
         }
@@ -395,6 +420,7 @@
         @Override
         public boolean shouldOverrideUrlLoading(WebView view, String url) {
             mLastShouldOverrideUrl = url;
+            mShouldOverrideUrlLoadingCallCount++;
             return false;
         }
     }
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
index 1bec05c..cfe1a81 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
@@ -16,6 +16,7 @@
 
 package android.webkit.cts;
 
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.AssetManager;
 import android.cts.util.EvaluateJsResultPollingCheck;
@@ -79,7 +80,9 @@
 import java.io.IOException;
 import java.util.Collections;
 import java.util.Date;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.FutureTask;
 import java.util.concurrent.TimeUnit;
 import java.util.HashMap;
@@ -140,7 +143,7 @@
     protected void tearDown() throws Exception {
         mOnUiThread.cleanUp();
         if (mWebServer != null) {
-            mWebServer.shutdown();
+            stopWebServer();
         }
         if (mIconDb != null) {
             mIconDb.removeAllIcons();
@@ -358,6 +361,18 @@
     }
 
     @UiThreadTest
+    public void testLoadUrlDoesNotStripParamsWhenLoadingContentUrls() throws Exception {
+        Uri.Builder uriBuilder = new Uri.Builder().scheme(
+                ContentResolver.SCHEME_CONTENT).authority(MockContentProvider.AUTHORITY);
+        uriBuilder.appendPath("foo.html").appendQueryParameter("param","bar");
+        String url = uriBuilder.build().toString();
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        // verify the parameter is not stripped.
+        Uri uri = Uri.parse(mWebView.getTitle());
+        assertEquals("bar", uri.getQueryParameter("param"));
+    }
+
+    @UiThreadTest
     public void testAppInjectedXRequestedWithHeaderIsNotOverwritten() throws Exception {
         startWebServer(false);
         String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
@@ -651,6 +666,42 @@
         assertEquals("removedObject", resultObject.getResult());
     }
 
+    public void testAddJavascriptInterfaceExceptions() throws Exception {
+        WebSettings settings = mOnUiThread.getSettings();
+        settings.setJavaScriptEnabled(true);
+        settings.setJavaScriptCanOpenWindowsAutomatically(true);
+
+        final AtomicBoolean mJsInterfaceWasCalled = new AtomicBoolean(false) {
+            @JavascriptInterface
+            public synchronized void call() {
+                set(true);
+                // The main purpose of this test is to ensure an exception here does not
+                // crash the implementation.
+                throw new RuntimeException("Javascript Interface exception");
+            }
+        };
+
+        mOnUiThread.addJavascriptInterface(mJsInterfaceWasCalled, "dummy");
+
+        mOnUiThread.loadUrlAndWaitForCompletion("about:blank");
+
+        assertFalse(mJsInterfaceWasCalled.get());
+
+        final CountDownLatch resultLatch = new CountDownLatch(1);
+        mOnUiThread.evaluateJavascript(
+                "try {dummy.call(); 'fail'; } catch (exception) { 'pass'; } ",
+                new ValueCallback<String>() {
+                        @Override
+                        public void onReceiveValue(String result) {
+                            assertEquals("\"pass\"", result);
+                            resultLatch.countDown();
+                        }
+                    });
+
+        assertTrue(resultLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
+        assertTrue(mJsInterfaceWasCalled.get());
+    }
+
     public void testCapturePicture() throws Exception, Throwable {
         startWebServer(false);
         final String url = mWebServer.getAssetUrl(TestHtmlConstants.BLANK_PAGE_URL);
@@ -1438,16 +1489,6 @@
     }
 
     @UiThreadTest
-    public void testGetFavicon() throws Exception {
-        startWebServer(false);
-        String url = mWebServer.getAssetUrl(TestHtmlConstants.TEST_FAVICON_URL);
-        mOnUiThread.loadUrlAndWaitForCompletion(url);
-        mWebView.getFavicon();
-        // ToBeFixed: Favicon is not loaded automatically.
-        // assertNotNull(mWebView.getFavicon());
-    }
-
-    @UiThreadTest
     public void testClearHistory() throws Exception {
         startWebServer(false);
         String url1 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1);
@@ -1509,7 +1550,7 @@
         assertNotNull(restoreList);
         assertEquals(3, restoreList.getSize());
         assertEquals(2, saveList.getCurrentIndex());
-        /* ToBeFixed: The WebHistoryItems do not get inflated. Uncomment remaining tests when fixed.
+
         // wait for the list items to get inflated
         new PollingCheck(TEST_TIMEOUT) {
             @Override
@@ -1530,7 +1571,6 @@
         assertEquals(url1, copyListAfterRestore.getItemAtIndex(0).getUrl());
         assertEquals(url2, copyListAfterRestore.getItemAtIndex(1).getUrl());
         assertEquals(url3, copyListAfterRestore.getItemAtIndex(2).getUrl());
-        */
     }
 
     public void testSetWebViewClient() throws Throwable {
@@ -1743,6 +1783,35 @@
         assertEquals("Second page", mOnUiThread.getTitle());
     }
 
+    public void testSecureServerRequestingClientCertDoesNotCancelRequest() throws Throwable {
+        mWebServer = new CtsTestServer(getActivity(), CtsTestServer.SslMode.WANTS_CLIENT_AUTH);
+        final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
+        final SslErrorWebViewClient webViewClient = new SslErrorWebViewClient();
+        mOnUiThread.setWebViewClient(webViewClient);
+        mOnUiThread.clearSslPreferences();
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        // Page loaded OK...
+        assertTrue(webViewClient.wasOnReceivedSslErrorCalled());
+        assertEquals(TestHtmlConstants.HELLO_WORLD_TITLE, mOnUiThread.getTitle());
+        assertEquals(0, webViewClient.onReceivedErrorCode());
+    }
+
+    public void testSecureServerRequiringClientCertDoesCancelRequest() throws Throwable {
+        mWebServer = new CtsTestServer(getActivity(), CtsTestServer.SslMode.NEEDS_CLIENT_AUTH);
+        final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
+        final SslErrorWebViewClient webViewClient = new SslErrorWebViewClient();
+        mOnUiThread.setWebViewClient(webViewClient);
+        mOnUiThread.clearSslPreferences();
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        // Page NOT loaded OK...
+        // In this case, we must NOT have received the onReceivedSslError callback as that is for
+        // recoverable (e.g. server auth) errors, whereas failing mandatory client auth is non-
+        // recoverable and should drop straight through to a load error.
+        assertFalse(webViewClient.wasOnReceivedSslErrorCalled());
+        assertFalse(TestHtmlConstants.HELLO_WORLD_TITLE.equals(mOnUiThread.getTitle()));
+        assertEquals(WebViewClient.ERROR_FAILED_SSL_HANDSHAKE, webViewClient.onReceivedErrorCode());
+    }
+
     public void testRequestChildRectangleOnScreen() throws Throwable {
         DisplayMetrics metrics = mOnUiThread.getDisplayMetrics();
         final int dimension = 2 * Math.max(metrics.widthPixels, metrics.heightPixels);
@@ -1767,12 +1836,12 @@
     }
 
     public void testSetDownloadListener() throws Throwable {
+        final CountDownLatch resultLatch = new CountDownLatch(1);
         final class MyDownloadListener implements DownloadListener {
             public String url;
             public String mimeType;
             public long contentLength;
             public String contentDisposition;
-            public boolean called;
 
             @Override
             public void onDownloadStart(String url, String userAgent, String contentDisposition,
@@ -1781,7 +1850,7 @@
                 this.mimeType = mimetype;
                 this.contentLength = contentLength;
                 this.contentDisposition = contentDisposition;
-                this.called = true;
+                resultLatch.countDown();
             }
         }
 
@@ -1804,17 +1873,11 @@
         // Wait for layout to complete before setting focus.
         getInstrumentation().waitForIdleSync();
 
-        new PollingCheck(TEST_TIMEOUT) {
-            @Override
-            protected boolean check() {
-                return listener.called;
-            }
-        }.run();
+        assertTrue(resultLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
         assertEquals(url, listener.url);
         assertTrue(listener.contentDisposition.contains("test.bin"));
-        // ToBeFixed: uncomment the following tests after fixing the framework
-        // assertEquals(mimeType, listener.mimeType);
-        // assertEquals(length, listener.contentLength);
+        assertEquals(length, listener.contentLength);
+        assertEquals(mimeType, listener.mimeType);
     }
 
     @UiThreadTest
@@ -2077,11 +2140,6 @@
         });
     }
 
-    @UiThreadTest
-    public void testInternals() {
-        // Do not test these APIs. They are implementation details.
-    }
-
     private static class HrefCheckHandler extends Handler {
         private boolean mHadRecieved;
 
@@ -2165,51 +2223,6 @@
             }
     }
 
-    // Find b1 inside b2
-    private boolean checkBitmapInsideAnother(Bitmap b1, Bitmap b2) {
-        int w = b1.getWidth();
-        int h = b1.getHeight();
-
-        for (int i = 0; i < (b2.getWidth()-w+1); i++) {
-            for (int j = 0; j < (b2.getHeight()-h+1); j++) {
-                if (checkBitmapInsideAnother(b1, b2, i, j))
-                    return true;
-            }
-        }
-        return false;
-    }
-
-    private boolean comparePixel(int p1, int p2, int maxError) {
-        int err;
-        err = Math.abs(((p1&0xff000000)>>>24) - ((p2&0xff000000)>>>24));
-        if (err > maxError)
-            return false;
-
-        err = Math.abs(((p1&0x00ff0000)>>>16) - ((p2&0x00ff0000)>>>16));
-        if (err > maxError)
-            return false;
-
-        err = Math.abs(((p1&0x0000ff00)>>>8) - ((p2&0x0000ff00)>>>8));
-        if (err > maxError)
-            return false;
-
-        err = Math.abs(((p1&0x000000ff)>>>0) - ((p2&0x000000ff)>>>0));
-        if (err > maxError)
-            return false;
-
-        return true;
-    }
-
-    private boolean checkBitmapInsideAnother(Bitmap b1, Bitmap b2, int x, int y) {
-        for (int i = 0; i < b1.getWidth(); i++)
-            for (int j = 0; j < b1.getHeight(); j++) {
-                if (!comparePixel(b1.getPixel(i, j), b2.getPixel(x + i, y + j), 10)) {
-                    return false;
-                }
-            }
-        return true;
-    }
-
     /**
      * Waits at least MIN_SCROLL_WAIT_MS for scrolling to start. Once started,
      * scrolling is checked every SCROLL_WAIT_INTERVAL_MS for changes. Once
@@ -2239,6 +2252,7 @@
     final class SslErrorWebViewClient extends WaitForLoadedClient {
         private boolean mWasOnReceivedSslErrorCalled;
         private String mErrorUrl;
+        private int mErrorCode;
 
         public SslErrorWebViewClient() {
             super(mOnUiThread);
@@ -2249,6 +2263,11 @@
             mErrorUrl = error.getUrl();
             handler.proceed();
         }
+        @Override
+        public void onReceivedError(WebView view, int errorCode, String description,
+                String failingUrl) {
+            mErrorCode = errorCode;
+        }
         public void resetWasOnReceivedSslErrorCalled() {
             mWasOnReceivedSslErrorCalled = false;
         }
@@ -2258,6 +2277,9 @@
         public String errorUrl() {
             return mErrorUrl;
         }
+        public int onReceivedErrorCode() {
+            return mErrorCode;
+        }
     }
 
     final class ScaleChangedWebViewClient extends WaitForLoadedClient {
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 e56a7cf..1cc86ca 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
@@ -77,8 +77,7 @@
         sourcePath.add("./cts/tests/src");
         sourcePath.add("./cts/libs/commonutil/src");
         sourcePath.add("./cts/libs/deviceutil/src");
-        sourcePath.add("./frameworks/testing/uiautomator/library/testrunner-src");
-        sourcePath.add("./frameworks/testing/uiautomator_test_libraries/src");
+        sourcePath.add("./frameworks/uiautomator/src");
         sourcePath.add(sourceDir.toString());
         return join(sourcePath, ":");
     }
diff --git a/tools/tradefed-host/etc/cts-tradefed b/tools/tradefed-host/etc/cts-tradefed
index bc5c07a..485740e 100755
--- a/tools/tradefed-host/etc/cts-tradefed
+++ b/tools/tradefed-host/etc/cts-tradefed
@@ -51,17 +51,21 @@
 
 
 # check if in Android build env
-if [ ! -z ${ANDROID_BUILD_TOP} ]; then
-    HOST=`uname`
-    if [ "$HOST" == "Linux" ]; then
-        OS="linux-x86"
-    elif [ "$HOST" == "Darwin" ]; then
-        OS="darwin-x86"
+if [ ! -z "${ANDROID_BUILD_TOP}" ]; then
+    if [ ! -z "${ANDROID_HOST_OUT}" ]; then
+      CTS_ROOT=${ANDROID_HOST_OUT}/cts
     else
-        echo "Unrecognized OS"
-        exit
-    fi;
-    CTS_ROOT=${ANDROID_BUILD_TOP}/out/host/${OS}/cts
+      HOST=`uname`
+      if [ "$HOST" == "Linux" ]; then
+          OS="linux-x86"
+      elif [ "$HOST" == "Darwin" ]; then
+          OS="darwin-x86"
+      else
+          echo "Unrecognized OS"
+          exit
+      fi
+      CTS_ROOT=${ANDROID_BUILD_TOP}/${OUT_DIR:-out}/host/${OS}/cts
+    fi
     if [ ! -d ${CTS_ROOT} ]; then
         echo "Could not find $CTS_ROOT in Android build environment. Try 'make cts'"
         exit
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java
index c6977e5..48f4773 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java
@@ -43,14 +43,13 @@
 import com.android.tradefed.util.RunUtil;
 import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
 
+import junit.framework.Test;
+
 import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.InputStream;
-import java.lang.InterruptedException;
-import java.lang.System;
-import java.lang.Thread;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -63,8 +62,6 @@
 import java.util.Queue;
 import java.util.Set;
 
-import junit.framework.Test;
-
 /**
  * A {@link Test} for running CTS tests.
  * <p/>
@@ -600,8 +597,13 @@
             for (String uri : plan.getTestUris()) {
                 if (!mExcludedPackageNames.contains(uri)) {
                     ITestPackageDef testPackage = testRepo.getTestPackage(uri);
-                    testPackage.setExcludedTestFilter(plan.getExcludedTestFilter(uri));
-                    testPkgDefs.add(testPackage);
+                    if (testPackage != null) {
+                        testPackage.setExcludedTestFilter(plan.getExcludedTestFilter(uri));
+                        testPkgDefs.add(testPackage);
+                    } else {
+                        CLog.e("Could not find test package uri %s referenced in plan %s", uri,
+                                mPlanName);
+                    }
                 }
             }
         } else if (mPackageNames.size() > 0){
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PrintTestRemoteTestRunner.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PrintTestRemoteTestRunner.java
new file mode 100644
index 0000000..3d92eb3
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PrintTestRemoteTestRunner.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2014 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.AdbCommandRejectedException;
+import com.android.ddmlib.IShellEnabledDevice;
+import com.android.ddmlib.Log;
+import com.android.ddmlib.ShellCommandUnresponsiveException;
+import com.android.ddmlib.TimeoutException;
+import com.android.ddmlib.testrunner.IRemoteAndroidTestRunner;
+import com.android.ddmlib.testrunner.ITestRunListener;
+import com.android.ddmlib.testrunner.InstrumentationResultParser;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.TimeUnit;
+
+public class PrintTestRemoteTestRunner implements IRemoteAndroidTestRunner {
+
+    private final String mPackageName;
+    private final String mRunnerName;
+    private IShellEnabledDevice mRemoteDevice;
+    // default to no timeout
+    private long mMaxTimeToOutputResponse = 0;
+    private TimeUnit mMaxTimeUnits = TimeUnit.MILLISECONDS;
+    private String mRunName = null;
+
+    /** map of name-value instrumentation argument pairs */
+    private Map<String, String> mArgMap;
+    private InstrumentationResultParser mParser;
+
+    private static final String LOG_TAG = "RemoteAndroidTest";
+    private static final String DEFAULT_RUNNER_NAME = "android.test.InstrumentationTestRunner";
+
+    private static final char CLASS_SEPARATOR = ',';
+    private static final char METHOD_SEPARATOR = '#';
+    private static final char RUNNER_SEPARATOR = '/';
+
+    // defined instrumentation argument names
+    private static final String CLASS_ARG_NAME = "class";
+    private static final String LOG_ARG_NAME = "log";
+    private static final String DEBUG_ARG_NAME = "debug";
+    private static final String COVERAGE_ARG_NAME = "coverage";
+    private static final String PACKAGE_ARG_NAME = "package";
+    private static final String SIZE_ARG_NAME = "size";
+
+    // This command starts a shell Java program (installed by this class)
+    // in the folder owned by the shell user. This app creates a proxy
+    // which does privileged operations such as wiping a package's user
+    // data and then starts the tests passing the proxy. This enables
+    // the tests to clear the print spooler data.
+    private static final String INSTRUMENTATION_COMMAND =
+            "chmod 755 /data/local/tmp/print-instrument && "
+            + "/data/local/tmp/print-instrument instrument -w -r %1$s %2$s";
+
+    /**
+     * Creates a remote Android test runner.
+     *
+     * @param packageName the Android application package that contains the
+     *            tests to run
+     * @param runnerName the instrumentation test runner to execute. If null,
+     *            will use default runner
+     * @param remoteDevice the Android device to execute tests on
+     */
+    public PrintTestRemoteTestRunner(String packageName, String runnerName,
+            IShellEnabledDevice remoteDevice) {
+
+        mPackageName = packageName;
+        mRunnerName = runnerName;
+        mRemoteDevice = remoteDevice;
+        mArgMap = new Hashtable<String, String>();
+    }
+
+    /**
+     * Alternate constructor. Uses default instrumentation runner.
+     *
+     * @param packageName the Android application package that contains the
+     *            tests to run
+     * @param remoteDevice the Android device to execute tests on
+     */
+    public PrintTestRemoteTestRunner(String packageName, IShellEnabledDevice remoteDevice) {
+        this(packageName, null, remoteDevice);
+    }
+
+    @Override
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    @Override
+    public String getRunnerName() {
+        if (mRunnerName == null) {
+            return DEFAULT_RUNNER_NAME;
+        }
+        return mRunnerName;
+    }
+
+    /**
+     * Returns the complete instrumentation component path.
+     */
+    private String getRunnerPath() {
+        return getPackageName() + RUNNER_SEPARATOR + getRunnerName();
+    }
+
+    @Override
+    public void setClassName(String className) {
+        addInstrumentationArg(CLASS_ARG_NAME, className);
+    }
+
+    @Override
+    public void setClassNames(String[] classNames) {
+        StringBuilder classArgBuilder = new StringBuilder();
+
+        for (int i = 0; i < classNames.length; i++) {
+            if (i != 0) {
+                classArgBuilder.append(CLASS_SEPARATOR);
+            }
+            classArgBuilder.append(classNames[i]);
+        }
+        setClassName(classArgBuilder.toString());
+    }
+
+    @Override
+    public void setMethodName(String className, String testName) {
+        setClassName(className + METHOD_SEPARATOR + testName);
+    }
+
+    @Override
+    public void setTestPackageName(String packageName) {
+        addInstrumentationArg(PACKAGE_ARG_NAME, packageName);
+    }
+
+    @Override
+    public void addInstrumentationArg(String name, String value) {
+        if (name == null || value == null) {
+            throw new IllegalArgumentException("name or value arguments cannot be null");
+        }
+        mArgMap.put(name, value);
+    }
+
+    @Override
+    public void removeInstrumentationArg(String name) {
+        if (name == null) {
+            throw new IllegalArgumentException("name argument cannot be null");
+        }
+        mArgMap.remove(name);
+    }
+
+    @Override
+    public void addBooleanArg(String name, boolean value) {
+        addInstrumentationArg(name, Boolean.toString(value));
+    }
+
+    @Override
+    public void setLogOnly(boolean logOnly) {
+        addBooleanArg(LOG_ARG_NAME, logOnly);
+    }
+
+    @Override
+    public void setDebug(boolean debug) {
+        addBooleanArg(DEBUG_ARG_NAME, debug);
+    }
+
+    @Override
+    public void setCoverage(boolean coverage) {
+        addBooleanArg(COVERAGE_ARG_NAME, coverage);
+    }
+
+    @Override
+    public void setTestSize(TestSize size) {
+        addInstrumentationArg(SIZE_ARG_NAME, ""/*size.getRunnerValue()*/);
+    }
+
+    @Override
+    public void setMaxtimeToOutputResponse(int maxTimeToOutputResponse) {
+        setMaxTimeToOutputResponse(maxTimeToOutputResponse, TimeUnit.MILLISECONDS);
+    }
+
+    @Override
+    public void setMaxTimeToOutputResponse(long maxTimeToOutputResponse, TimeUnit maxTimeUnits) {
+        mMaxTimeToOutputResponse = maxTimeToOutputResponse;
+        mMaxTimeUnits = maxTimeUnits;
+    }
+
+    @Override
+    public void setRunName(String runName) {
+        mRunName = runName;
+    }
+
+    @Override
+    public void run(ITestRunListener... listeners) throws TimeoutException,
+            AdbCommandRejectedException, ShellCommandUnresponsiveException, IOException {
+        run(Arrays.asList(listeners));
+    }
+
+    @Override
+    public void run(Collection<ITestRunListener> listeners) throws TimeoutException,
+            AdbCommandRejectedException, ShellCommandUnresponsiveException, IOException {
+        final String runCaseCommandStr = String.format(INSTRUMENTATION_COMMAND,
+              getArgsCommand(), getRunnerPath());
+        Log.i(LOG_TAG,
+                String.format("Running %1$s on %2$s", runCaseCommandStr, mRemoteDevice.getName()));
+        String runName = mRunName == null ? mPackageName : mRunName;
+        mParser = new InstrumentationResultParser(runName, listeners);
+
+        try {
+            mRemoteDevice.executeShellCommand(runCaseCommandStr, mParser, mMaxTimeToOutputResponse,
+                    mMaxTimeUnits);
+        } catch (IOException e) {
+            Log.w(LOG_TAG, String.format("IOException %1$s when running tests %2$s on %3$s",
+                    e.toString(), getPackageName(), mRemoteDevice.getName()));
+            // rely on parser to communicate results to listeners
+            mParser.handleTestRunFailed(e.toString());
+            throw e;
+        } catch (ShellCommandUnresponsiveException e) {
+            Log.w(LOG_TAG, String.format(
+                    "ShellCommandUnresponsiveException %1$s when running tests %2$s on %3$s",
+                    e.toString(), getPackageName(), mRemoteDevice.getName()));
+            mParser.handleTestRunFailed(String
+                    .format("Failed to receive adb shell test output within %1$d ms. "
+                            + "Test may have timed out, or adb connection to device became"
+                            + "unresponsive", mMaxTimeToOutputResponse));
+            throw e;
+        } catch (TimeoutException e) {
+            Log.w(LOG_TAG, String.format("TimeoutException when running tests %1$s on %2$s",
+                    getPackageName(), mRemoteDevice.getName()));
+            mParser.handleTestRunFailed(e.toString());
+            throw e;
+        } catch (AdbCommandRejectedException e) {
+            Log.w(LOG_TAG, String.format(
+                    "AdbCommandRejectedException %1$s when running tests %2$s on %3$s",
+                    e.toString(), getPackageName(), mRemoteDevice.getName()));
+            mParser.handleTestRunFailed(e.toString());
+            throw e;
+        }
+    }
+
+    @Override
+    public void cancel() {
+        if (mParser != null) {
+            mParser.cancel();
+        }
+    }
+
+    /**
+     * Returns the full instrumentation command line syntax for the provided
+     * instrumentation arguments. Returns an empty string if no arguments were
+     * specified.
+     */
+    private String getArgsCommand() {
+        StringBuilder commandBuilder = new StringBuilder();
+        for (Entry<String, String> argPair : mArgMap.entrySet()) {
+            final String argCmd = String.format(" -e %1$s %2$s", argPair.getKey(),
+                    argPair.getValue());
+            commandBuilder.append(argCmd);
+        }
+        return commandBuilder.toString();
+    }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PrintTestRunner.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PrintTestRunner.java
new file mode 100644
index 0000000..a7a6ccc
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PrintTestRunner.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2014 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.cts.tradefed.targetprep.SettingsToggler;
+import com.android.ddmlib.testrunner.IRemoteAndroidTestRunner;
+import com.android.ddmlib.testrunner.IRemoteAndroidTestRunner.TestSize;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.testtype.IDeviceTest;
+import com.android.tradefed.testtype.IRemoteTest;
+import com.android.tradefed.util.StringEscapeUtils;
+
+import java.io.FileNotFoundException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Running the print tests requires modification of secure settings. 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 driver is used for enabling these services, running
+ * the tests, and disabling the services.
+ */
+public class PrintTestRunner implements IBuildReceiver, IRemoteTest, IDeviceTest  {
+
+    private static final String PRINT_TEST_AND_SERVICES_APP_NAME =
+            "CtsPrintTestCases.apk";
+
+    private static final String PRINT_TESTS_PACKAGE_NAME =
+            "com.android.cts.print";
+
+    private static final String FIRST_PRINT_SERVICE_NAME =
+            "android.print.cts.services.FirstPrintService";
+
+    private static final String SECOND_PRINT_SERVICE_NAME =
+            "android.print.cts.services.SecondPrintService";
+
+    private static final String SHELL_USER_FOLDER = "data/local/tmp";
+
+    private static final String PRINT_INSTRUMENT_JAR = "CtsPrintInstrument.jar";
+
+    private static final String PRINT_INSTRUMENT_SCRIPT = "print-instrument";
+
+    private ITestDevice mDevice;
+
+    private CtsBuildHelper mCtsBuild;
+
+    private String mPackageName;
+    private String mRunnerName = "android.test.InstrumentationTestRunner";
+    private String mTestClassName;
+    private String mTestMethodName;
+    private String mTestPackageName;
+    private int mTestTimeout = 10 * 60 * 1000;  // 10 minutes
+    private String mTestSize;
+    private String mRunName = null;
+    private Map<String, String> mInstrArgMap = new HashMap<String, String>();
+
+    @Override
+    public void setBuild(IBuildInfo buildInfo) {
+        mCtsBuild = CtsBuildHelper.createBuildHelper(buildInfo);
+    }
+
+    @Override
+    public void setDevice(ITestDevice device) {
+        mDevice = device;
+    }
+
+    @Override
+    public ITestDevice getDevice() {
+        return mDevice;
+    }
+
+    public void setPackageName(String packageName) {
+        mPackageName = packageName;
+    }
+
+    public void setRunnerName(String runnerName) {
+        mRunnerName = runnerName;
+    }
+
+    public void setClassName(String testClassName) {
+        mTestClassName = testClassName;
+    }
+
+    public void setMethodName(String testMethodName) {
+        mTestMethodName = StringEscapeUtils.escapeShell(testMethodName);
+    }
+
+    public void setTestPackageName(String testPackageName) {
+        mTestPackageName = testPackageName;
+    }
+
+    public void setTestSize(String size) {
+        mTestSize = size;
+    }
+
+    public void setRunName(String runName) {
+        mRunName = runName;
+    }
+
+    @Override
+    public void run(final ITestInvocationListener listener) throws DeviceNotAvailableException {
+        installShellProgramAndScriptFiles();
+        installTestsAndServicesApk();
+        enablePrintServices();
+        doRunTests(listener);
+        disablePrintServices();
+        uninstallTestsAndServicesApk();
+        uninstallShellProgramAndScriptFiles();
+    }
+
+    private void doRunTests(ITestInvocationListener listener)
+            throws DeviceNotAvailableException {
+        if (mPackageName == null) {
+            throw new IllegalArgumentException("package name has not been set");
+        }
+        if (mDevice == null) {
+            throw new IllegalArgumentException("Device has not been set");
+        }
+
+        IRemoteAndroidTestRunner runner =  new PrintTestRemoteTestRunner(mPackageName,
+                mRunnerName, mDevice.getIDevice());
+
+        if (mTestClassName != null) {
+            if (mTestMethodName != null) {
+                runner.setMethodName(mTestClassName, mTestMethodName);
+            } else {
+                runner.setClassName(mTestClassName);
+            }
+        } else if (mTestPackageName != null) {
+            runner.setTestPackageName(mTestPackageName);
+        }
+        if (mTestSize != null) {
+            runner.setTestSize(TestSize.getTestSize(mTestSize));
+        }
+        runner.setMaxTimeToOutputResponse(mTestTimeout, TimeUnit.MILLISECONDS);
+        if (mRunName != null) {
+            runner.setRunName(mRunName);
+        }
+        for (Map.Entry<String, String> argEntry : mInstrArgMap.entrySet()) {
+            runner.addInstrumentationArg(argEntry.getKey(), argEntry.getValue());
+        }
+
+        mDevice.runInstrumentationTests(runner, listener);
+    }
+
+    private void installShellProgramAndScriptFiles() throws DeviceNotAvailableException {
+        installFile(PRINT_INSTRUMENT_JAR);
+        installFile(PRINT_INSTRUMENT_SCRIPT);
+    }
+
+    private void installFile(String fileName) throws DeviceNotAvailableException {
+        try {
+            final boolean success = getDevice().pushFile(mCtsBuild.getTestApp(
+                    fileName), SHELL_USER_FOLDER + "/" + fileName);
+            if (!success) {
+                throw new IllegalArgumentException("Failed to install "
+                        + fileName + " on " + getDevice().getSerialNumber());
+           }
+        } catch (FileNotFoundException fnfe) {
+            throw new IllegalArgumentException("Cannot find file: " + fileName);
+        }
+    }
+
+    private void uninstallShellProgramAndScriptFiles() throws DeviceNotAvailableException {
+        getDevice().executeShellCommand("rm " + SHELL_USER_FOLDER + "/"
+                + PRINT_INSTRUMENT_JAR);
+        getDevice().executeShellCommand("rm " + SHELL_USER_FOLDER + "/"
+                + PRINT_INSTRUMENT_SCRIPT);
+    }
+
+    private void installTestsAndServicesApk() throws DeviceNotAvailableException {
+        try {
+            String installCode = getDevice().installPackage(mCtsBuild.getTestApp(
+                    PRINT_TEST_AND_SERVICES_APP_NAME), true);
+            if (installCode != null) {
+                throw new IllegalArgumentException("Failed to install "
+                        + PRINT_TEST_AND_SERVICES_APP_NAME + " on " + getDevice().getSerialNumber()
+                        + ". Reason: " + installCode);
+           }
+        } catch (FileNotFoundException fnfe) {
+            throw new IllegalArgumentException("Cannot find file: "
+                    + PRINT_TEST_AND_SERVICES_APP_NAME);
+        }
+    }
+
+    private void uninstallTestsAndServicesApk() throws DeviceNotAvailableException {
+        getDevice().uninstallPackage(PRINT_TESTS_PACKAGE_NAME);
+    }
+
+    private void enablePrintServices() throws DeviceNotAvailableException {
+        String enabledServicesValue = PRINT_TESTS_PACKAGE_NAME + "/" + FIRST_PRINT_SERVICE_NAME
+                + ":" + PRINT_TESTS_PACKAGE_NAME + "/" + SECOND_PRINT_SERVICE_NAME;
+        SettingsToggler.setSecureString(getDevice(), "enabled_print_services",
+                enabledServicesValue);
+    }
+
+    private void disablePrintServices() throws DeviceNotAvailableException {
+        SettingsToggler.setSecureString(getDevice(), "enabled_print_services", "");
+    }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestFilter.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestFilter.java
index 053b265..448f067 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestFilter.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestFilter.java
@@ -25,6 +25,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.regex.Pattern;
 
 /**
  * Filter for {@link TestIdentifier}s.
@@ -34,7 +35,7 @@
     private final Set<String> mExcludedClasses;
     private final Set<TestIdentifier> mExcludedTests;
     private String mIncludedClass = null;
-    private String mIncludedMethod = null;
+    private Pattern mIncludedMethod = null;
 
     /**
      * Creates a {@link TestFilter}
@@ -87,7 +88,9 @@
      */
     public void setTestInclusion(String className, String method) {
         mIncludedClass = className;
-        mIncludedMethod = method;
+        if (method != null) {
+            mIncludedMethod = Pattern.compile(method);
+        }
     }
 
     /**
@@ -103,7 +106,7 @@
                 // skip
                 continue;
             }
-            if (mIncludedMethod != null && !test.getTestName().equals(mIncludedMethod)) {
+            if (mIncludedMethod != null && !mIncludedMethod.matcher(test.getTestName()).matches()) {
                 // skip
                 continue;
             }
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 8ab5d18..994da0b 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
@@ -47,9 +47,11 @@
     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";
+            "com.android.cts.tradefed.testtype.AccessibilityTestRunner";
     public static final String ACCESSIBILITY_SERVICE_TEST =
-        "com.android.cts.tradefed.testtype.AccessibilityServiceTestRunner";
+            "com.android.cts.tradefed.testtype.AccessibilityServiceTestRunner";
+    public static final String PRINT_TEST =
+            "com.android.cts.tradefed.testtype.PrintTestRunner";
     public static final String DISPLAY_TEST =
             "com.android.cts.tradefed.testtype.DisplayTestRunner";
     public static final String UIAUTOMATOR_TEST = "uiAutomator";
@@ -61,7 +63,6 @@
     private String mAppNameSpace = null;
     private String mName = null;
     private String mRunner = null;
-    private boolean mIsVMHostTest = false;
     private String mTestType = null;
     private String mJarPath = null;
     private boolean mIsSignatureTest = false;
@@ -230,6 +231,9 @@
         } else if (ACCESSIBILITY_TEST.equals(mTestType)) {
             AccessibilityTestRunner test = new AccessibilityTestRunner();
             return setInstrumentationTest(test, testCaseDir);
+        } else if (PRINT_TEST.equals(mTestType)) {
+            PrintTestRunner test = new PrintTestRunner();
+            return setPrintTest(test, testCaseDir);
         } else if (ACCESSIBILITY_SERVICE_TEST.equals(mTestType)) {
             AccessibilityServiceTestRunner test = new AccessibilityServiceTestRunner();
             return setInstrumentationTest(test, testCaseDir);
@@ -270,6 +274,18 @@
         }
     }
 
+    private PrintTestRunner setPrintTest(PrintTestRunner printTest,
+            File testCaseDir) {
+        printTest.setRunName(getUri());
+        printTest.setPackageName(mAppNameSpace);
+        printTest.setRunnerName(mRunner);
+        printTest.setTestPackageName(mTestPackageName);
+        printTest.setClassName(mClassName);
+        printTest.setMethodName(mMethodName);
+        mDigest = generateDigest(testCaseDir, String.format("%s.apk", mName));
+        return printTest;
+    }
+
     /**
      * Populates given {@link InstrumentationApkTest} with data from the package xml.
      *
diff --git a/tools/utils/buildCts.py b/tools/utils/buildCts.py
index b269be8..cb70c54 100755
--- a/tools/utils/buildCts.py
+++ b/tools/utils/buildCts.py
@@ -126,8 +126,14 @@
 
     plan = tools.TestPlan(packages)
     plan.Include(r'android\.core\.tests.*')
+    plan.Exclude(r'android\.core\.tests\.libcore.\package.\harmony*')
     self.__WritePlan(plan, 'Java')
 
+    # TODO: remove this once the tests are fixed and merged into Java plan above.
+    plan = tools.TestPlan(packages)
+    plan.Include(r'android\.core\.tests\.libcore.\package.\harmony*')
+    self.__WritePlan(plan, 'Harmony')
+
     plan = tools.TestPlan(packages)
     plan.Include(r'android\.core\.vm-tests-tf')
     self.__WritePlan(plan, 'VM-TF')
diff --git a/tools/vm-tests-tf/etc/starttests b/tools/vm-tests-tf/etc/starttests
new file mode 100755
index 0000000..0c8721b
--- /dev/null
+++ b/tools/vm-tests-tf/etc/starttests
@@ -0,0 +1,296 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+
+prog="$0"
+while [ -h "${prog}" ]; do
+    newProg=`/bin/ls -ld "${prog}"`
+    newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+    if expr "x${newProg}" : 'x/' >/dev/null; then
+        prog="${newProg}"
+    else
+        progdir=`dirname "${prog}"`
+        prog="${progdir}/${newProg}"
+    fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+libdir=`dirname $progdir`/framework
+
+javaOpts=""
+while expr "x$1" : 'x-J' >/dev/null; do
+    opt=`expr "$1" : '-J\(.*\)'`
+    javaOpts="${javaOpts} -${opt}"
+    shift
+done
+
+
+#######################################################################
+# Original content of invocation script follows. Uses values cleverly
+# deduced by the above code.
+#######################################################################
+
+selection=$1
+interpreter="fast"
+if [ "$selection" = "--portable" ]; then
+    selection=$2;
+    interpreter="portable"
+fi
+
+dalviktest=$ANDROID_BUILD_TOP/out/host/common/obj/JAVA_LIBRARIES/vm-tests-tf_intermediates
+dalviktestdir=$dalviktest/tests
+dexcore=$dalviktest/tests/dot/junit/dexcore.jar
+scriptdata=$dalviktestdir/data/scriptdata
+report=$dalviktest/report.html
+curdate=`date`
+curmode=""
+datadir=/tmp/${USER}
+base=$OUT
+framework=$base/system/framework
+export ANDROID_PRINTF_LOG=tag
+export ANDROID_LOG_TAGS='*:s' # was: jdwp:i dalvikvm:i dalvikvmi:i'
+export ANDROID_DATA=$datadir
+export ANDROID_ROOT=$base/system
+export LD_LIBRARY_PATH=$base/system/lib
+export DYLD_LIBRARY_PATH=$base/system/lib
+debug_opts="-Xcheck:jni -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n"
+exe=$base/system/bin/dalvikvm
+bpath=$framework/core.jar
+
+echo "--------------------------------------------------"
+echo "Dalvik VM Test Suite"
+echo "Version 1.0"
+echo "Copyright (c) 2008 The Android Open Source Project"
+echo ""
+
+if [ "$selection" = "--help" ]; then
+    echo "Usage: vm-tests [--help|--portable] [<mnemonic>]"
+    echo ""
+    echo "    --help      prints this help message"
+    echo "    --portable  uses the portable interpreter;"
+    echo "                default is the fast one"
+    echo ""
+    echo "    <mnemonic>  specifies the instruction to test;"
+    echo "                default is to run all tests"
+    echo ""
+    exit 1;
+fi
+
+rm -rf --preserve-root $datadir/dalvik-cache
+mkdir -p $datadir
+mkdir -p $datadir/dalvik-cache
+
+if [ "$TARGET_SIMULATOR" = "true" ]; then
+    echo "Simulator mode, $interpreter interpreter";
+    curmode="simulator"
+else
+    echo "Emulator mode, $interpreter interpreter";
+    curmode="emulator"
+fi
+
+echo ""
+
+pre_report="<html><head><style>
+table tr.ok { background:#a0ffa0; }
+table tr.nok { background:#ffa0a0; }
+table tr.wok { background:#ffffa0; }
+table tr.lok { background:#aaaaff; }
+</style></head>
+<body>
+<h1>Dalvik VM test suite results</h1>
+Generated $curdate (using the $curmode)
+<p>
+<table width='100%'>
+<tr><td>Status</td><td>Target</td><td>Category</td><td>Details</td></tr>"
+post_report="</body></html>"
+
+rm -f $report
+echo $pre_report > $report
+
+# ----------- running each opcode test ------------
+
+export jpassedcnt=0
+export jfailedcnt=0
+export jvfefailedcnt=0
+export jwarningcnt=0
+export jallcnt=0
+export jcolumns=0
+
+# TODO unhack
+if [ "$TARGET_SIMULATOR" = "true" ]; then
+    echo -n ""
+else
+    adb push $dexcore /data/local/tmp/dexcore.jar >> /dev/null 2>&1
+fi
+
+function classnameToJar()
+{
+    echo $1 | sed -e 's#\.#/#g;s#$#.jar#'
+}
+
+while read -u 3 myline;
+do
+    # dot.junit.opcodes.add_double.Main_testB1;dot.junit.opcodes.add_double.d.T_add_double_1 ;opcode add_double;test B #1 (border edge case)
+    # ->
+    # mainclass: dot.junit.opcodes.add_double.Main_testB1
+    # testcasedir: opcodes/add_double
+    # testname: testB1 ->
+    # dir dot/junit/opcodes/add_double/testB1
+
+    # e.g dot.junit.opcodes.add_double.Main_testB1
+    mainclass=`echo $myline | cut -d";" -f1`
+    # e.g dot.junit.opcodes.add_double.d.T_add_double_1, space sep. >=1 entries
+    deps=`echo $myline | cut -d";" -f2`
+
+    jtitle=`echo $myline | cut -d";" -f3`
+    jcomment=`echo $myline | cut -d";" -f4`
+    details=`echo $myline | cut -d";" -f5`
+
+    if [ "$selection" == "" ] || [ "$jtitle" == "$selection" ]; then
+
+        (( jallcnt += 1 ))
+
+        cd $dalviktestdir
+        rm -f $datadir/dalvikout
+        # write dalvik output to file
+        echo -n "mk_b:" > $datadir/dalvikout
+
+        if [ "$TARGET_SIMULATOR" = "true" ]; then
+            classpath=`classnameToJar ${mainclass}`
+            for dep in ${deps}; do
+                depJar=`classnameToJar ${dep}`
+                classpath=${classpath}:${depJar}
+            done
+            $valgrind $exe -Xint:$interpreter -Xmx512M -Xss32K -Xbootclasspath:$bpath $debug_opts \
+                -classpath $dexcore:$classpath $mainclass >> $datadir/dalvikout 2>&1
+
+            RESULTCODE=$?
+            if [ ${RESULTCODE} -ne 0 ]; then
+                echo "Dalvik VM failed, result=${RESULTCODE}" >> $datadir/dalvikout 2>&1
+            fi
+        else
+            classpath="/data/local/tmp/dexcore.jar"
+            deps=${deps}" "${mainclass}
+            pushedjars=""
+            for dep in ${deps}; do
+                depJar=`classnameToJar ${dep}`
+                depFileName=`basename ${depJar}`
+                deviceFileName=/data/local/tmp/${depFileName}
+                adb push ${depJar} ${deviceFileName} &> /dev/null
+                classpath=${classpath}:${deviceFileName}
+                pushedjars=${pushedjars}" "${deviceFileName}
+            done
+
+            adb shell dalvikvm -Djava.io.tmpdir=/data/local/tmp \
+                -classpath $classpath $mainclass >> $datadir/dalvikout 2>&1 && \
+                echo -n dvmpassed: >> $datadir/dalvikout 2>&1
+
+            for jar in ${pushedjars}; do
+                adb shell rm ${jar} &> /dev/null
+            done
+        fi
+
+        echo -n "mk_s:" >> $datadir/dalvikout
+        # Verify tmpout only contains mkdxc_start;mkdxc_stop -> no system.out/err
+        # because of exception. If ok -> green report line else red report with info
+        # between mkdxc_start and stop
+        vmresult=`cat $datadir/dalvikout`
+
+        if [[ ("$vmresult" == "mk_b:mk_s:") || ("$vmresult" == "mk_b:dvmpassed:mk_s:") ]]; then
+            (( jpassedcnt += 1 ))
+            echo -n "<tr class=\"ok\"><td>Success</td><td>$jtitle</td>" >> $report
+            echo "<td>$jcomment</td><td>$details</td></tr>" >> $report
+            echo -n "."
+        else
+            vmres=`cat $datadir/dalvikout | sed -e 's/mk_b://;s/mk_s://'`
+            vmres="$details<br><pre>$vmres</pre>"
+
+            stacktraces=`echo $vmresult | grep "java\.lang\." | grep -c "at dot\.junit\."`
+            if [[ $stacktraces > 0 ]]; then
+                jtype=`echo "$mainclass" | sed -e 's/.*_test\([^0-9]*\)[0-9].*/\1/' `
+                if [ "$jtype" == "VFE" ]; then
+                    (( jvfefailedcnt += 1 ))
+                    echo -n "V"
+                else
+                    (( jfailedcnt += 1 ))
+                    echo -n "F"
+                fi
+
+                echo "<tr class=\"nok\"><td>Failure</td><td>$jtitle</td><td>" >> $report
+                echo "$jcomment</td><td>$vmres</td></tr>" >> $report
+            else
+                (( jwarningcnt += 1 ))
+                echo "<tr class=\"wok\"><td>Failure</td><td>$jtitle</td><td>" >> $report
+                echo "$jcomment</td><td>(No stacktrace, but errors on console)" >> $report
+                echo "<br>$vmres</td></tr>" >> $report
+                echo -n "C"
+            fi
+        fi
+
+        (( jcolumns += 1 ))
+        if [ ${jcolumns} -eq 40 ]; then
+            echo ""
+            (( jcolumns = 0 ))
+        fi
+
+    fi
+# Use fd nr 3 to avoid subshelling via cat since this looses all
+# variables(and thus also the counters we are interested in).
+done 3<$scriptdata
+
+echo "</table>" >> $report
+let jallcalccnt=$jpassedcnt+$jfailedcnt+$jvfefailedcnt+$jwarningcnt
+if [ $jallcalccnt -ne $jallcnt ]; then
+    echo "<br>error: green & red != total , $jallcalccnt -ne $jallcnt" >> $report
+    exit 1;
+fi
+
+echo $post_report >> $report
+
+echo "<br>Tests run: ${jallcnt}" >> $report
+echo "<br>Functional failures: ${jfailedcnt}" >> $report
+echo "<br>Verifier failures: ${jvfefailedcnt}" >> $report
+echo "<br>Console errors: ${jwarningcnt}" >> $report
+
+echo $post_report >> $report
+
+if [[ jcolumns -ne 0 ]]; then
+    echo ""
+fi
+
+echo ""
+
+if [[ jallcnt -eq jpassedcnt ]]; then
+    echo "OK (${jpassedcnt} tests)"
+else
+    echo "FAILURES!!!"
+    echo ""
+    echo "Tests run          : ${jallcnt}"
+    echo "Functional failures: ${jfailedcnt}"
+    echo "Verifier failures  : ${jvfefailedcnt}"
+    echo "Console errors     : ${jwarningcnt}"
+fi
+
+echo ""
+echo "Please see complete report in ${report}"
+echo "--------------------------------------------------"
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_virtual/d/T_invoke_virtual_12.d b/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_virtual/d/T_invoke_virtual_12.d
index 02d509a..8068732 100644
--- a/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_virtual/d/T_invoke_virtual_12.d
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_virtual/d/T_invoke_virtual_12.d
@@ -25,6 +25,7 @@
 .end method
 
 .method public test(Ljava/lang/String;)V
+.limit regs 2
     return-void
 .end method
 
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_virtual_range/d/T_invoke_virtual_range_12.d b/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_virtual_range/d/T_invoke_virtual_range_12.d
index 9b63ef8..93df0f5 100644
--- a/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_virtual_range/d/T_invoke_virtual_range_12.d
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_virtual_range/d/T_invoke_virtual_range_12.d
@@ -25,6 +25,7 @@
 .end method
 
 .method public test(Ljava/lang/String;)V
+.limit regs 2
     return-void
 .end method
 
diff --git a/tools/vm-tests-tf/src/util/build/BuildDalvikSuite.java b/tools/vm-tests-tf/src/util/build/BuildDalvikSuite.java
index 9d4bbed..d18ff4f 100644
--- a/tools/vm-tests-tf/src/util/build/BuildDalvikSuite.java
+++ b/tools/vm-tests-tf/src/util/build/BuildDalvikSuite.java
@@ -260,6 +260,7 @@
     private void handleTests() throws IOException {
         System.out.println("collected " + testMethodsCnt + " test methods in " +
                 testClassCnt + " junit test classes");
+        String datafileContent = "";
         Set<BuildStep> targets = new TreeSet<BuildStep>();
 
         javacHostJunitBuildStep = new JavacBuildStep(HOSTJUNIT_CLASSES_OUTPUT_FOLDER, CLASS_PATH);
@@ -336,6 +337,86 @@
                 targets.add(dexBuildStep);
                 // }
 
+
+                // prepare the entry in the data file for the bash script.
+                // e.g.
+                // main class to execute; opcode/constraint; test purpose
+                // dxc.junit.opcodes.aaload.Main_testN1;aaload;normal case test
+                // (#1)
+
+                char ca = method.charAt("test".length()); // either N,B,E,
+                // or V (VFE)
+                String comment;
+                switch (ca) {
+                case 'N':
+                    comment = "Normal #" + method.substring(5);
+                    break;
+                case 'B':
+                    comment = "Boundary #" + method.substring(5);
+                    break;
+                case 'E':
+                    comment = "Exception #" + method.substring(5);
+                    break;
+                case 'V':
+                    comment = "Verifier #" + method.substring(7);
+                    break;
+                default:
+                    throw new RuntimeException("unknown test abbreviation:"
+                            + method + " for " + fqcn);
+                }
+
+                String line = pName + ".Main_" + method + ";";
+                for (String className : dependentTestClassNames) {
+                    line += className + " ";
+                }
+
+
+                // test description
+                String[] pparts = pName.split("\\.");
+                // detail e.g. add_double
+                String detail = pparts[pparts.length-1];
+                // type := opcode | verify
+                String type = pparts[pparts.length-2];
+
+                String description;
+                if ("format".equals(type)) {
+                    description = "format";
+                } else if ("opcodes".equals(type)) {
+                    // Beautify name, so it matches the actual mnemonic
+                    detail = detail.replaceAll("_", "-");
+                    detail = detail.replace("-from16", "/from16");
+                    detail = detail.replace("-high16", "/high16");
+                    detail = detail.replace("-lit8", "/lit8");
+                    detail = detail.replace("-lit16", "/lit16");
+                    detail = detail.replace("-4", "/4");
+                    detail = detail.replace("-16", "/16");
+                    detail = detail.replace("-32", "/32");
+                    detail = detail.replace("-jumbo", "/jumbo");
+                    detail = detail.replace("-range", "/range");
+                    detail = detail.replace("-2addr", "/2addr");
+
+                    // Unescape reserved words
+                    detail = detail.replace("opc-", "");
+
+                    description = detail;
+                } else if ("verify".equals(type)) {
+                    description = "verifier";
+                } else {
+                    description = type + " " + detail;
+                }
+
+                String details = (md.title != null ? md.title : "");
+                if (md.constraint != null) {
+                    details = " Constraint " + md.constraint + ", " + details;
+                }
+                if (details.length() != 0) {
+                    details = details.substring(0, 1).toUpperCase()
+                            + details.substring(1);
+                }
+
+                line += ";" + description + ";" + comment + ";" + details;
+
+                datafileContent += line + "\n";
                 generateBuildStepFor(pName, method, dependentTestClassNames,
                         targets);
             }
@@ -346,6 +427,10 @@
         // write latest HOSTJUNIT generated file.
         flushHostJunitFile();
 
+        File scriptDataDir = new File(OUTPUT_FOLDER + "/data/");
+        scriptDataDir.mkdirs();
+        writeToFile(new File(scriptDataDir, "scriptdata"), datafileContent);
+
         if (!javacHostJunitBuildStep.build()) {
             System.out.println("main javac cts-host-hostjunit-classes build step failed");
             System.exit(1);